mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-09 23:24:05 -08:00
Merge branch 'main' into cedwards/ooo-native-histograms
Some checks are pending
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Build Prometheus for common architectures (0) (push) Waiting to run
CI / Build Prometheus for common architectures (1) (push) Waiting to run
CI / Build Prometheus for common architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (0) (push) Waiting to run
CI / Build Prometheus for all architectures (1) (push) Waiting to run
CI / Build Prometheus for all architectures (10) (push) Waiting to run
CI / Build Prometheus for all architectures (11) (push) Waiting to run
CI / Build Prometheus for all architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (3) (push) Waiting to run
CI / Build Prometheus for all architectures (4) (push) Waiting to run
CI / Build Prometheus for all architectures (5) (push) Waiting to run
CI / Build Prometheus for all architectures (6) (push) Waiting to run
CI / Build Prometheus for all architectures (7) (push) Waiting to run
CI / Build Prometheus for all architectures (8) (push) Waiting to run
CI / Build Prometheus for all architectures (9) (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
Some checks are pending
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Build Prometheus for common architectures (0) (push) Waiting to run
CI / Build Prometheus for common architectures (1) (push) Waiting to run
CI / Build Prometheus for common architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (0) (push) Waiting to run
CI / Build Prometheus for all architectures (1) (push) Waiting to run
CI / Build Prometheus for all architectures (10) (push) Waiting to run
CI / Build Prometheus for all architectures (11) (push) Waiting to run
CI / Build Prometheus for all architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (3) (push) Waiting to run
CI / Build Prometheus for all architectures (4) (push) Waiting to run
CI / Build Prometheus for all architectures (5) (push) Waiting to run
CI / Build Prometheus for all architectures (6) (push) Waiting to run
CI / Build Prometheus for all architectures (7) (push) Waiting to run
CI / Build Prometheus for all architectures (8) (push) Waiting to run
CI / Build Prometheus for all architectures (9) (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
This commit is contained in:
commit
e2eb411328
56
.github/stale.yml
vendored
56
.github/stale.yml
vendored
|
@ -1,56 +0,0 @@
|
|||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 60
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: false
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
- keepalive
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: false
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
# unmarkComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
# closeComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: pulls
|
||||
|
||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||
# pulls:
|
||||
# daysUntilStale: 30
|
||||
# markComment: >
|
||||
# This pull request has been automatically marked as stale because it has not had
|
||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
||||
# for your contributions.
|
||||
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - confirmed
|
2
.github/workflows/buf-lint.yml
vendored
2
.github/workflows/buf-lint.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: bufbuild/buf-setup-action@aceb106d2419c4cff48863df90161d92decb8591 # v1.35.1
|
||||
- uses: bufbuild/buf-setup-action@54abbed4fe8d8d45173eca4798b0c39a53a7b658 # v1.39.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: bufbuild/buf-lint-action@06f9dd823d873146471cfaaf108a993fe00e5325 # v1.1.1
|
||||
|
|
2
.github/workflows/buf.yml
vendored
2
.github/workflows/buf.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
if: github.repository_owner == 'prometheus'
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: bufbuild/buf-setup-action@aceb106d2419c4cff48863df90161d92decb8591 # v1.35.1
|
||||
- uses: bufbuild/buf-setup-action@54abbed4fe8d8d45173eca4798b0c39a53a7b658 # v1.39.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: bufbuild/buf-lint-action@06f9dd823d873146471cfaaf108a993fe00e5325 # v1.1.1
|
||||
|
|
37
.github/workflows/ci.yml
vendored
37
.github/workflows/ci.yml
vendored
|
@ -11,10 +11,10 @@ jobs:
|
|||
container:
|
||||
# Whenever the Go version is updated here, .promu.yml
|
||||
# should also be updated.
|
||||
image: quay.io/prometheus/golang-builder:1.22-base
|
||||
image: quay.io/prometheus/golang-builder:1.23-base
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0
|
||||
- uses: prometheus/promci@45166329da36d74895901808f1c8c97efafc7f84 # v0.3.0
|
||||
- uses: ./.github/promci/actions/setup_environment
|
||||
- run: make GOOPTS=--tags=stringlabels GO_ONLY=1 SKIP_GOLANGCI_LINT=1
|
||||
- run: go test --tags=stringlabels ./tsdb/ -test.tsdb-isolation=false
|
||||
|
@ -25,10 +25,10 @@ jobs:
|
|||
name: More Go tests
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: quay.io/prometheus/golang-builder:1.22-base
|
||||
image: quay.io/prometheus/golang-builder:1.23-base
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0
|
||||
- uses: prometheus/promci@45166329da36d74895901808f1c8c97efafc7f84 # v0.3.0
|
||||
- uses: ./.github/promci/actions/setup_environment
|
||||
- run: go test --tags=dedupelabels ./...
|
||||
- run: GOARCH=386 go test ./cmd/prometheus
|
||||
|
@ -39,9 +39,12 @@ jobs:
|
|||
test_go_oldest:
|
||||
name: Go tests with previous Go version
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Enforce the Go version.
|
||||
GOTOOLCHAIN: local
|
||||
container:
|
||||
# The go version in this image should be N-1 wrt test_go.
|
||||
image: quay.io/prometheus/golang-builder:1.21-base
|
||||
image: quay.io/prometheus/golang-builder:1.22-base
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- run: make build
|
||||
|
@ -54,11 +57,11 @@ jobs:
|
|||
# Whenever the Go version is updated here, .promu.yml
|
||||
# should also be updated.
|
||||
container:
|
||||
image: quay.io/prometheus/golang-builder:1.22-base
|
||||
image: quay.io/prometheus/golang-builder:1.23-base
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0
|
||||
- uses: prometheus/promci@45166329da36d74895901808f1c8c97efafc7f84 # v0.3.0
|
||||
- uses: ./.github/promci/actions/setup_environment
|
||||
with:
|
||||
enable_go: false
|
||||
|
@ -77,7 +80,7 @@ jobs:
|
|||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
go-version: 1.23.x
|
||||
- run: |
|
||||
$TestTargets = go list ./... | Where-Object { $_ -NotMatch "(github.com/prometheus/prometheus/discovery.*|github.com/prometheus/prometheus/config|github.com/prometheus/prometheus/web)"}
|
||||
go test $TestTargets -vet=off -v
|
||||
|
@ -89,7 +92,7 @@ jobs:
|
|||
# Whenever the Go version is updated here, .promu.yml
|
||||
# should also be updated.
|
||||
container:
|
||||
image: quay.io/prometheus/golang-builder:1.22-base
|
||||
image: quay.io/prometheus/golang-builder:1.23-base
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- run: go install ./cmd/promtool/.
|
||||
|
@ -115,7 +118,7 @@ jobs:
|
|||
thread: [ 0, 1, 2 ]
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0
|
||||
- uses: prometheus/promci@45166329da36d74895901808f1c8c97efafc7f84 # v0.3.0
|
||||
- uses: ./.github/promci/actions/build
|
||||
with:
|
||||
promu_opts: "-p linux/amd64 -p windows/amd64 -p linux/arm64 -p darwin/amd64 -p darwin/arm64 -p linux/386"
|
||||
|
@ -138,7 +141,7 @@ jobs:
|
|||
# should also be updated.
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0
|
||||
- uses: prometheus/promci@45166329da36d74895901808f1c8c97efafc7f84 # v0.3.0
|
||||
- uses: ./.github/promci/actions/build
|
||||
with:
|
||||
parallelism: 12
|
||||
|
@ -165,7 +168,7 @@ jobs:
|
|||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
cache: false
|
||||
go-version: 1.22.x
|
||||
go-version: 1.23.x
|
||||
- name: Run goyacc and check for diff
|
||||
run: make install-goyacc check-generated-parser
|
||||
golangci:
|
||||
|
@ -177,7 +180,7 @@ jobs:
|
|||
- name: Install Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
go-version: 1.23.x
|
||||
- name: Install snmp_exporter/generator dependencies
|
||||
run: sudo apt-get update && sudo apt-get -y install libsnmp-dev
|
||||
if: github.repository == 'prometheus/snmp_exporter'
|
||||
|
@ -200,7 +203,7 @@ jobs:
|
|||
if: github.event_name == 'push' && github.event.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0
|
||||
- uses: prometheus/promci@45166329da36d74895901808f1c8c97efafc7f84 # v0.3.0
|
||||
- uses: ./.github/promci/actions/publish_main
|
||||
with:
|
||||
docker_hub_login: ${{ secrets.docker_hub_login }}
|
||||
|
@ -214,7 +217,7 @@ jobs:
|
|||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v2.')
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0
|
||||
- uses: prometheus/promci@45166329da36d74895901808f1c8c97efafc7f84 # v0.3.0
|
||||
- uses: ./.github/promci/actions/publish_release
|
||||
with:
|
||||
docker_hub_login: ${{ secrets.docker_hub_login }}
|
||||
|
@ -229,9 +232,9 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: prometheus/promci@3cb0c3871f223bd5ce1226995bd52ffb314798b6 # v0.1.0
|
||||
- uses: prometheus/promci@45166329da36d74895901808f1c8c97efafc7f84 # v0.3.0
|
||||
- name: Install nodejs
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
node-version-file: "web/ui/.nvmrc"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
|
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
|
@ -27,12 +27,12 @@ jobs:
|
|||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
|
||||
uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
|
||||
uses: github/codeql-action/autobuild@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
|
||||
uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
|
|
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
|
@ -45,6 +45,6 @@ jobs:
|
|||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # tag=v3.25.15
|
||||
uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # tag=v3.26.6
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
31
.github/workflows/stale.yml
vendored
Normal file
31
.github/workflows/stale.yml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
name: Stale Check
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
- cron: '16 22 * * *'
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
jobs:
|
||||
stale:
|
||||
if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# opt out of defaults to avoid marking issues as stale and closing them
|
||||
# https://github.com/actions/stale#days-before-close
|
||||
# https://github.com/actions/stale#days-before-stale
|
||||
days-before-stale: -1
|
||||
days-before-close: -1
|
||||
# Setting it to empty string to skip comments.
|
||||
# https://github.com/actions/stale#stale-pr-message
|
||||
# https://github.com/actions/stale#stale-issue-message
|
||||
stale-pr-message: ''
|
||||
stale-issue-message: ''
|
||||
operations-per-run: 30
|
||||
# override days-before-stale, for only marking the pull requests as stale
|
||||
days-before-pr-stale: 60
|
||||
stale-pr-label: stale
|
||||
exempt-pr-labels: keepalive
|
|
@ -1,7 +1,7 @@
|
|||
go:
|
||||
# Whenever the Go version is updated here,
|
||||
# .github/workflows should also be updated.
|
||||
version: 1.22
|
||||
version: 1.23
|
||||
repository:
|
||||
path: github.com/prometheus/prometheus
|
||||
build:
|
||||
|
|
|
@ -154,6 +154,9 @@ type flagConfig struct {
|
|||
RemoteFlushDeadline model.Duration
|
||||
nameEscapingScheme string
|
||||
|
||||
enableAutoReload bool
|
||||
autoReloadInterval model.Duration
|
||||
|
||||
featureList []string
|
||||
memlimitRatio float64
|
||||
// These options are extracted from featureList
|
||||
|
@ -169,6 +172,8 @@ type flagConfig struct {
|
|||
corsRegexString string
|
||||
|
||||
promlogConfig promlog.Config
|
||||
|
||||
promqlEnableDelayedNameRemoval bool
|
||||
}
|
||||
|
||||
// setFeatureListOptions sets the corresponding options from the featureList.
|
||||
|
@ -210,6 +215,12 @@ func (c *flagConfig) setFeatureListOptions(logger log.Logger) error {
|
|||
case "auto-gomaxprocs":
|
||||
c.enableAutoGOMAXPROCS = true
|
||||
level.Info(logger).Log("msg", "Automatically set GOMAXPROCS to match Linux container CPU quota")
|
||||
case "auto-reload-config":
|
||||
c.enableAutoReload = true
|
||||
if s := time.Duration(c.autoReloadInterval).Seconds(); s > 0 && s < 1 {
|
||||
c.autoReloadInterval, _ = model.ParseDuration("1s")
|
||||
}
|
||||
level.Info(logger).Log("msg", fmt.Sprintf("Enabled automatic configuration file reloading. Checking for configuration changes every %s.", c.autoReloadInterval))
|
||||
case "auto-gomemlimit":
|
||||
c.enableAutoGOMEMLIMIT = true
|
||||
level.Info(logger).Log("msg", "Automatically set GOMEMLIMIT to match Linux container or system memory limit")
|
||||
|
@ -241,6 +252,9 @@ func (c *flagConfig) setFeatureListOptions(logger log.Logger) error {
|
|||
case "delayed-compaction":
|
||||
c.tsdb.EnableDelayedCompaction = true
|
||||
level.Info(logger).Log("msg", "Experimental delayed compaction is enabled.")
|
||||
case "promql-delayed-name-removal":
|
||||
c.promqlEnableDelayedNameRemoval = true
|
||||
level.Info(logger).Log("msg", "Experimental PromQL delayed name removal enabled.")
|
||||
case "utf8-names":
|
||||
model.NameValidationScheme = model.UTF8Validation
|
||||
level.Info(logger).Log("msg", "Experimental UTF-8 support enabled")
|
||||
|
@ -300,6 +314,9 @@ func main() {
|
|||
a.Flag("config.file", "Prometheus configuration file path.").
|
||||
Default("prometheus.yml").StringVar(&cfg.configFile)
|
||||
|
||||
a.Flag("config.auto-reload-interval", "Specifies the interval for checking and automatically reloading the Prometheus configuration file upon detecting changes.").
|
||||
Default("30s").SetValue(&cfg.autoReloadInterval)
|
||||
|
||||
a.Flag("web.listen-address", "Address to listen on for UI, API, and telemetry. Can be repeated.").
|
||||
Default("0.0.0.0:9090").StringsVar(&cfg.web.ListenAddresses)
|
||||
|
||||
|
@ -490,7 +507,7 @@ func main() {
|
|||
|
||||
a.Flag("scrape.name-escaping-scheme", `Method for escaping legacy invalid names when sending to Prometheus that does not support UTF-8. Can be one of "values", "underscores", or "dots".`).Default(scrape.DefaultNameEscapingScheme.String()).StringVar(&cfg.nameEscapingScheme)
|
||||
|
||||
a.Flag("enable-feature", "Comma separated feature names to enable. Valid options: agent, auto-gomemlimit, exemplar-storage, expand-external-labels, memory-snapshot-on-shutdown, promql-per-step-stats, promql-experimental-functions, remote-write-receiver (DEPRECATED), extra-scrape-metrics, new-service-discovery-manager, auto-gomaxprocs, no-default-scrape-port, native-histograms, otlp-write-receiver, created-timestamp-zero-ingestion, concurrent-rule-eval, delayed-compaction, utf8-names. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details.").
|
||||
a.Flag("enable-feature", "Comma separated feature names to enable. Valid options: agent, auto-gomaxprocs, auto-gomemlimit, auto-reload-config, concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, expand-external-labels, extra-scrape-metrics, memory-snapshot-on-shutdown, native-histograms, new-service-discovery-manager, no-default-scrape-port, otlp-write-receiver, promql-experimental-functions, promql-delayed-name-removal, promql-per-step-stats, remote-write-receiver (DEPRECATED), utf8-names. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details.").
|
||||
Default("").StringsVar(&cfg.featureList)
|
||||
|
||||
promlogflag.AddFlags(a, &cfg.promlogConfig)
|
||||
|
@ -802,9 +819,10 @@ func main() {
|
|||
NoStepSubqueryIntervalFn: noStepSubqueryInterval.Get,
|
||||
// EnableAtModifier and EnableNegativeOffset have to be
|
||||
// always on for regular PromQL as of Prometheus v2.33.
|
||||
EnableAtModifier: true,
|
||||
EnableNegativeOffset: true,
|
||||
EnablePerStepStats: cfg.enablePerStepStats,
|
||||
EnableAtModifier: true,
|
||||
EnableNegativeOffset: true,
|
||||
EnablePerStepStats: cfg.enablePerStepStats,
|
||||
EnableDelayedNameRemoval: cfg.promqlEnableDelayedNameRemoval,
|
||||
}
|
||||
|
||||
queryEngine = promql.NewEngine(opts)
|
||||
|
@ -996,12 +1014,18 @@ func main() {
|
|||
listeners, err := webHandler.Listeners()
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "Unable to start web listeners", "err", err)
|
||||
if err := queryEngine.Close(); err != nil {
|
||||
level.Warn(logger).Log("msg", "Closing query engine failed", "err", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = toolkit_web.Validate(*webConfig)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "Unable to validate web configuration file", "err", err)
|
||||
if err := queryEngine.Close(); err != nil {
|
||||
level.Warn(logger).Log("msg", "Closing query engine failed", "err", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
@ -1023,6 +1047,9 @@ func main() {
|
|||
case <-cancel:
|
||||
reloadReady.Close()
|
||||
}
|
||||
if err := queryEngine.Close(); err != nil {
|
||||
level.Warn(logger).Log("msg", "Closing query engine failed", "err", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func(err error) {
|
||||
|
@ -1117,6 +1144,15 @@ func main() {
|
|||
hup := make(chan os.Signal, 1)
|
||||
signal.Notify(hup, syscall.SIGHUP)
|
||||
cancel := make(chan struct{})
|
||||
|
||||
var checksum string
|
||||
if cfg.enableAutoReload {
|
||||
checksum, err = config.GenerateChecksum(cfg.configFile)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "Failed to generate initial checksum for configuration file", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
g.Add(
|
||||
func() error {
|
||||
<-reloadReady.C
|
||||
|
@ -1126,6 +1162,12 @@ func main() {
|
|||
case <-hup:
|
||||
if err := reloadConfig(cfg.configFile, cfg.enableExpandExternalLabels, cfg.tsdb.EnableExemplarStorage, logger, noStepSubqueryInterval, reloaders...); err != nil {
|
||||
level.Error(logger).Log("msg", "Error reloading config", "err", err)
|
||||
} else if cfg.enableAutoReload {
|
||||
if currentChecksum, err := config.GenerateChecksum(cfg.configFile); err == nil {
|
||||
checksum = currentChecksum
|
||||
} else {
|
||||
level.Error(logger).Log("msg", "Failed to generate checksum during configuration reload", "err", err)
|
||||
}
|
||||
}
|
||||
case rc := <-webHandler.Reload():
|
||||
if err := reloadConfig(cfg.configFile, cfg.enableExpandExternalLabels, cfg.tsdb.EnableExemplarStorage, logger, noStepSubqueryInterval, reloaders...); err != nil {
|
||||
|
@ -1133,6 +1175,32 @@ func main() {
|
|||
rc <- err
|
||||
} else {
|
||||
rc <- nil
|
||||
if cfg.enableAutoReload {
|
||||
if currentChecksum, err := config.GenerateChecksum(cfg.configFile); err == nil {
|
||||
checksum = currentChecksum
|
||||
} else {
|
||||
level.Error(logger).Log("msg", "Failed to generate checksum during configuration reload", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
case <-time.Tick(time.Duration(cfg.autoReloadInterval)):
|
||||
if !cfg.enableAutoReload {
|
||||
continue
|
||||
}
|
||||
currentChecksum, err := config.GenerateChecksum(cfg.configFile)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "Failed to generate checksum during configuration reload", "err", err)
|
||||
continue
|
||||
}
|
||||
if currentChecksum == checksum {
|
||||
continue
|
||||
}
|
||||
level.Info(logger).Log("msg", "Configuration file change detected, reloading the configuration.")
|
||||
|
||||
if err := reloadConfig(cfg.configFile, cfg.enableExpandExternalLabels, cfg.tsdb.EnableExemplarStorage, logger, noStepSubqueryInterval, reloaders...); err != nil {
|
||||
level.Error(logger).Log("msg", "Error reloading config", "err", err)
|
||||
} else {
|
||||
checksum = currentChecksum
|
||||
}
|
||||
case <-cancel:
|
||||
return nil
|
||||
|
|
|
@ -236,14 +236,14 @@ func main() {
|
|||
|
||||
tsdbDumpCmd := tsdbCmd.Command("dump", "Dump samples from a TSDB.")
|
||||
dumpPath := tsdbDumpCmd.Arg("db path", "Database path (default is "+defaultDBPath+").").Default(defaultDBPath).String()
|
||||
dumpSandboxDirRoot := tsdbDumpCmd.Flag("sandbox-dir-root", "Root directory where a sandbox directory would be created in case WAL replay generates chunks. The sandbox directory is cleaned up at the end.").Default(defaultDBPath).String()
|
||||
dumpSandboxDirRoot := tsdbDumpCmd.Flag("sandbox-dir-root", "Root directory where a sandbox directory will be created, this sandbox is used in case WAL replay generates chunks (default is the database path). The sandbox is cleaned up at the end.").String()
|
||||
dumpMinTime := tsdbDumpCmd.Flag("min-time", "Minimum timestamp to dump.").Default(strconv.FormatInt(math.MinInt64, 10)).Int64()
|
||||
dumpMaxTime := tsdbDumpCmd.Flag("max-time", "Maximum timestamp to dump.").Default(strconv.FormatInt(math.MaxInt64, 10)).Int64()
|
||||
dumpMatch := tsdbDumpCmd.Flag("match", "Series selector. Can be specified multiple times.").Default("{__name__=~'(?s:.*)'}").Strings()
|
||||
|
||||
tsdbDumpOpenMetricsCmd := tsdbCmd.Command("dump-openmetrics", "[Experimental] Dump samples from a TSDB into OpenMetrics text format, excluding native histograms and staleness markers, which are not representable in OpenMetrics.")
|
||||
dumpOpenMetricsPath := tsdbDumpOpenMetricsCmd.Arg("db path", "Database path (default is "+defaultDBPath+").").Default(defaultDBPath).String()
|
||||
dumpOpenMetricsSandboxDirRoot := tsdbDumpOpenMetricsCmd.Flag("sandbox-dir-root", "Root directory where a sandbox directory would be created in case WAL replay generates chunks. The sandbox directory is cleaned up at the end.").Default(defaultDBPath).String()
|
||||
dumpOpenMetricsSandboxDirRoot := tsdbDumpOpenMetricsCmd.Flag("sandbox-dir-root", "Root directory where a sandbox directory will be created, this sandbox is used in case WAL replay generates chunks (default is the database path). The sandbox is cleaned up at the end.").String()
|
||||
dumpOpenMetricsMinTime := tsdbDumpOpenMetricsCmd.Flag("min-time", "Minimum timestamp to dump.").Default(strconv.FormatInt(math.MinInt64, 10)).Int64()
|
||||
dumpOpenMetricsMaxTime := tsdbDumpOpenMetricsCmd.Flag("max-time", "Maximum timestamp to dump.").Default(strconv.FormatInt(math.MaxInt64, 10)).Int64()
|
||||
dumpOpenMetricsMatch := tsdbDumpOpenMetricsCmd.Flag("match", "Series selector. Can be specified multiple times.").Default("{__name__=~'(?s:.*)'}").Strings()
|
||||
|
|
|
@ -35,6 +35,7 @@ import (
|
|||
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/model/rulefmt"
|
||||
"github.com/prometheus/prometheus/promql/promqltest"
|
||||
)
|
||||
|
||||
var promtoolPath = os.Args[0]
|
||||
|
@ -549,3 +550,46 @@ func TestCheckRulesWithRuleFiles(t *testing.T) {
|
|||
require.Equal(t, lintErrExitCode, exitCode, "")
|
||||
})
|
||||
}
|
||||
|
||||
func TestTSDBDumpCommand(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
|
||||
storage := promqltest.LoadedStorage(t, `
|
||||
load 1m
|
||||
metric{foo="bar"} 1 2 3
|
||||
`)
|
||||
t.Cleanup(func() { storage.Close() })
|
||||
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
subCmd string
|
||||
sandboxDirRoot string
|
||||
}{
|
||||
{
|
||||
name: "dump",
|
||||
subCmd: "dump",
|
||||
},
|
||||
{
|
||||
name: "dump with sandbox dir root",
|
||||
subCmd: "dump",
|
||||
sandboxDirRoot: t.TempDir(),
|
||||
},
|
||||
{
|
||||
name: "dump-openmetrics",
|
||||
subCmd: "dump-openmetrics",
|
||||
},
|
||||
{
|
||||
name: "dump-openmetrics with sandbox dir root",
|
||||
subCmd: "dump-openmetrics",
|
||||
sandboxDirRoot: t.TempDir(),
|
||||
},
|
||||
} {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
args := []string{"-test.main", "tsdb", c.subCmd, storage.Dir()}
|
||||
cmd := exec.Command(promtoolPath, args...)
|
||||
require.NoError(t, cmd.Run())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func TestGenerateBucket(t *testing.T) {
|
|||
}
|
||||
|
||||
// getDumpedSamples dumps samples and returns them.
|
||||
func getDumpedSamples(t *testing.T, path string, mint, maxt int64, match []string, formatter SeriesSetFormatter) string {
|
||||
func getDumpedSamples(t *testing.T, databasePath, sandboxDirRoot string, mint, maxt int64, match []string, formatter SeriesSetFormatter) string {
|
||||
t.Helper()
|
||||
|
||||
oldStdout := os.Stdout
|
||||
|
@ -64,8 +64,8 @@ func getDumpedSamples(t *testing.T, path string, mint, maxt int64, match []strin
|
|||
|
||||
err := dumpSamples(
|
||||
context.Background(),
|
||||
path,
|
||||
t.TempDir(),
|
||||
databasePath,
|
||||
sandboxDirRoot,
|
||||
mint,
|
||||
maxt,
|
||||
match,
|
||||
|
@ -96,13 +96,15 @@ func TestTSDBDump(t *testing.T) {
|
|||
heavy_metric{foo="bar"} 5 4 3 2 1
|
||||
heavy_metric{foo="foo"} 5 4 3 2 1
|
||||
`)
|
||||
t.Cleanup(func() { storage.Close() })
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mint int64
|
||||
maxt int64
|
||||
match []string
|
||||
expectedDump string
|
||||
name string
|
||||
mint int64
|
||||
maxt int64
|
||||
sandboxDirRoot string
|
||||
match []string
|
||||
expectedDump string
|
||||
}{
|
||||
{
|
||||
name: "default match",
|
||||
|
@ -111,6 +113,14 @@ func TestTSDBDump(t *testing.T) {
|
|||
match: []string{"{__name__=~'(?s:.*)'}"},
|
||||
expectedDump: "testdata/dump-test-1.prom",
|
||||
},
|
||||
{
|
||||
name: "default match with sandbox dir root set",
|
||||
mint: math.MinInt64,
|
||||
maxt: math.MaxInt64,
|
||||
sandboxDirRoot: t.TempDir(),
|
||||
match: []string{"{__name__=~'(?s:.*)'}"},
|
||||
expectedDump: "testdata/dump-test-1.prom",
|
||||
},
|
||||
{
|
||||
name: "same matcher twice",
|
||||
mint: math.MinInt64,
|
||||
|
@ -149,7 +159,7 @@ func TestTSDBDump(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dumpedMetrics := getDumpedSamples(t, storage.Dir(), tt.mint, tt.maxt, tt.match, formatSeriesSet)
|
||||
dumpedMetrics := getDumpedSamples(t, storage.Dir(), tt.sandboxDirRoot, tt.mint, tt.maxt, tt.match, formatSeriesSet)
|
||||
expectedMetrics, err := os.ReadFile(tt.expectedDump)
|
||||
require.NoError(t, err)
|
||||
expectedMetrics = normalizeNewLine(expectedMetrics)
|
||||
|
@ -171,12 +181,29 @@ func TestTSDBDumpOpenMetrics(t *testing.T) {
|
|||
my_counter{foo="bar", baz="abc"} 1 2 3 4 5
|
||||
my_gauge{bar="foo", abc="baz"} 9 8 0 4 7
|
||||
`)
|
||||
t.Cleanup(func() { storage.Close() })
|
||||
|
||||
expectedMetrics, err := os.ReadFile("testdata/dump-openmetrics-test.prom")
|
||||
require.NoError(t, err)
|
||||
expectedMetrics = normalizeNewLine(expectedMetrics)
|
||||
dumpedMetrics := getDumpedSamples(t, storage.Dir(), math.MinInt64, math.MaxInt64, []string{"{__name__=~'(?s:.*)'}"}, formatSeriesSetOpenMetrics)
|
||||
require.Equal(t, sortLines(string(expectedMetrics)), sortLines(dumpedMetrics))
|
||||
tests := []struct {
|
||||
name string
|
||||
sandboxDirRoot string
|
||||
}{
|
||||
{
|
||||
name: "default match",
|
||||
},
|
||||
{
|
||||
name: "default match with sandbox dir root set",
|
||||
sandboxDirRoot: t.TempDir(),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
expectedMetrics, err := os.ReadFile("testdata/dump-openmetrics-test.prom")
|
||||
require.NoError(t, err)
|
||||
expectedMetrics = normalizeNewLine(expectedMetrics)
|
||||
dumpedMetrics := getDumpedSamples(t, storage.Dir(), tt.sandboxDirRoot, math.MinInt64, math.MaxInt64, []string{"{__name__=~'(?s:.*)'}"}, formatSeriesSetOpenMetrics)
|
||||
require.Equal(t, sortLines(string(expectedMetrics)), sortLines(dumpedMetrics))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTSDBDumpOpenMetricsRoundTrip(t *testing.T) {
|
||||
|
@ -195,7 +222,7 @@ func TestTSDBDumpOpenMetricsRoundTrip(t *testing.T) {
|
|||
})
|
||||
|
||||
// Dump the blocks into OM format
|
||||
dumpedMetrics := getDumpedSamples(t, dbDir, math.MinInt64, math.MaxInt64, []string{"{__name__=~'(?s:.*)'}"}, formatSeriesSetOpenMetrics)
|
||||
dumpedMetrics := getDumpedSamples(t, dbDir, "", math.MinInt64, math.MaxInt64, []string{"{__name__=~'(?s:.*)'}"}, formatSeriesSetOpenMetrics)
|
||||
|
||||
// Should get back the initial metrics.
|
||||
require.Equal(t, string(initialMetrics), dumpedMetrics)
|
||||
|
|
|
@ -225,6 +225,15 @@ var expectedConf = &Config{
|
|||
TLSConfig: config.TLSConfig{
|
||||
MinVersion: config.TLSVersion(tls.VersionTLS10),
|
||||
},
|
||||
HTTPHeaders: &config.Headers{
|
||||
Headers: map[string]config.Header{
|
||||
"foo": {
|
||||
Values: []string{"foobar"},
|
||||
Secrets: []config.Secret{"bar", "foo"},
|
||||
Files: []string{filepath.FromSlash("testdata/valid_password_file")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
ServiceDiscoveryConfigs: discovery.Configs{
|
||||
|
@ -1532,7 +1541,7 @@ func TestElideSecrets(t *testing.T) {
|
|||
yamlConfig := string(config)
|
||||
|
||||
matches := secretRe.FindAllStringIndex(yamlConfig, -1)
|
||||
require.Len(t, matches, 22, "wrong number of secret matches found")
|
||||
require.Len(t, matches, 24, "wrong number of secret matches found")
|
||||
require.NotContains(t, yamlConfig, "mysecret",
|
||||
"yaml marshal reveals authentication credentials.")
|
||||
}
|
||||
|
|
92
config/reload.go
Normal file
92
config/reload.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2024 The Prometheus 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.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type ExternalFilesConfig struct {
|
||||
RuleFiles []string `yaml:"rule_files"`
|
||||
ScrapeConfigFiles []string `yaml:"scrape_config_files"`
|
||||
}
|
||||
|
||||
// GenerateChecksum generates a checksum of the YAML file and the files it references.
|
||||
func GenerateChecksum(yamlFilePath string) (string, error) {
|
||||
hash := sha256.New()
|
||||
|
||||
yamlContent, err := os.ReadFile(yamlFilePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error reading YAML file: %w", err)
|
||||
}
|
||||
_, err = hash.Write(yamlContent)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error writing YAML file to hash: %w", err)
|
||||
}
|
||||
|
||||
var config ExternalFilesConfig
|
||||
if err := yaml.Unmarshal(yamlContent, &config); err != nil {
|
||||
return "", fmt.Errorf("error unmarshalling YAML: %w", err)
|
||||
}
|
||||
|
||||
dir := filepath.Dir(yamlFilePath)
|
||||
|
||||
for i, file := range config.RuleFiles {
|
||||
config.RuleFiles[i] = filepath.Join(dir, file)
|
||||
}
|
||||
for i, file := range config.ScrapeConfigFiles {
|
||||
config.ScrapeConfigFiles[i] = filepath.Join(dir, file)
|
||||
}
|
||||
|
||||
files := map[string][]string{
|
||||
"r": config.RuleFiles, // "r" for rule files
|
||||
"s": config.ScrapeConfigFiles, // "s" for scrape config files
|
||||
}
|
||||
|
||||
for _, prefix := range []string{"r", "s"} {
|
||||
for _, pattern := range files[prefix] {
|
||||
matchingFiles, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error finding files with pattern %q: %w", pattern, err)
|
||||
}
|
||||
|
||||
for _, file := range matchingFiles {
|
||||
// Write prefix to the hash ("r" or "s") followed by \0, then
|
||||
// the file path.
|
||||
_, err = hash.Write([]byte(prefix + "\x00" + file + "\x00"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error writing %q path to hash: %w", file, err)
|
||||
}
|
||||
|
||||
// Read and hash the content of the file.
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error reading file %s: %w", file, err)
|
||||
}
|
||||
_, err = hash.Write(append(content, []byte("\x00")...))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error writing %q content to hash: %w", file, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hex.EncodeToString(hash.Sum(nil)), nil
|
||||
}
|
222
config/reload_test.go
Normal file
222
config/reload_test.go
Normal file
|
@ -0,0 +1,222 @@
|
|||
// Copyright 2024 The Prometheus 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.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGenerateChecksum(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Define paths for the temporary files.
|
||||
yamlFilePath := filepath.Join(tmpDir, "test.yml")
|
||||
ruleFilePath := filepath.Join(tmpDir, "rule_file.yml")
|
||||
scrapeConfigFilePath := filepath.Join(tmpDir, "scrape_config.yml")
|
||||
|
||||
// Define initial and modified content for the files.
|
||||
originalRuleContent := "groups:\n- name: example\n rules:\n - alert: ExampleAlert"
|
||||
modifiedRuleContent := "groups:\n- name: example\n rules:\n - alert: ModifiedAlert"
|
||||
|
||||
originalScrapeConfigContent := "scrape_configs:\n- job_name: example"
|
||||
modifiedScrapeConfigContent := "scrape_configs:\n- job_name: modified_example"
|
||||
|
||||
// Define YAML content referencing the rule and scrape config files.
|
||||
yamlContent := `
|
||||
rule_files:
|
||||
- rule_file.yml
|
||||
scrape_config_files:
|
||||
- scrape_config.yml
|
||||
`
|
||||
|
||||
// Write initial content to files.
|
||||
require.NoError(t, os.WriteFile(ruleFilePath, []byte(originalRuleContent), 0o644))
|
||||
require.NoError(t, os.WriteFile(scrapeConfigFilePath, []byte(originalScrapeConfigContent), 0o644))
|
||||
require.NoError(t, os.WriteFile(yamlFilePath, []byte(yamlContent), 0o644))
|
||||
|
||||
// Generate the original checksum.
|
||||
originalChecksum := calculateChecksum(t, yamlFilePath)
|
||||
|
||||
t.Run("Rule File Change", func(t *testing.T) {
|
||||
// Modify the rule file.
|
||||
require.NoError(t, os.WriteFile(ruleFilePath, []byte(modifiedRuleContent), 0o644))
|
||||
|
||||
// Checksum should change.
|
||||
modifiedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.NotEqual(t, originalChecksum, modifiedChecksum)
|
||||
|
||||
// Revert the rule file.
|
||||
require.NoError(t, os.WriteFile(ruleFilePath, []byte(originalRuleContent), 0o644))
|
||||
|
||||
// Checksum should return to the original.
|
||||
revertedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.Equal(t, originalChecksum, revertedChecksum)
|
||||
})
|
||||
|
||||
t.Run("Scrape Config Change", func(t *testing.T) {
|
||||
// Modify the scrape config file.
|
||||
require.NoError(t, os.WriteFile(scrapeConfigFilePath, []byte(modifiedScrapeConfigContent), 0o644))
|
||||
|
||||
// Checksum should change.
|
||||
modifiedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.NotEqual(t, originalChecksum, modifiedChecksum)
|
||||
|
||||
// Revert the scrape config file.
|
||||
require.NoError(t, os.WriteFile(scrapeConfigFilePath, []byte(originalScrapeConfigContent), 0o644))
|
||||
|
||||
// Checksum should return to the original.
|
||||
revertedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.Equal(t, originalChecksum, revertedChecksum)
|
||||
})
|
||||
|
||||
t.Run("Rule File Deletion", func(t *testing.T) {
|
||||
// Delete the rule file.
|
||||
require.NoError(t, os.Remove(ruleFilePath))
|
||||
|
||||
// Checksum should change.
|
||||
deletedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.NotEqual(t, originalChecksum, deletedChecksum)
|
||||
|
||||
// Restore the rule file.
|
||||
require.NoError(t, os.WriteFile(ruleFilePath, []byte(originalRuleContent), 0o644))
|
||||
|
||||
// Checksum should return to the original.
|
||||
revertedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.Equal(t, originalChecksum, revertedChecksum)
|
||||
})
|
||||
|
||||
t.Run("Scrape Config Deletion", func(t *testing.T) {
|
||||
// Delete the scrape config file.
|
||||
require.NoError(t, os.Remove(scrapeConfigFilePath))
|
||||
|
||||
// Checksum should change.
|
||||
deletedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.NotEqual(t, originalChecksum, deletedChecksum)
|
||||
|
||||
// Restore the scrape config file.
|
||||
require.NoError(t, os.WriteFile(scrapeConfigFilePath, []byte(originalScrapeConfigContent), 0o644))
|
||||
|
||||
// Checksum should return to the original.
|
||||
revertedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.Equal(t, originalChecksum, revertedChecksum)
|
||||
})
|
||||
|
||||
t.Run("Main File Change", func(t *testing.T) {
|
||||
// Modify the main YAML file.
|
||||
modifiedYamlContent := `
|
||||
global:
|
||||
scrape_interval: 3s
|
||||
rule_files:
|
||||
- rule_file.yml
|
||||
scrape_config_files:
|
||||
- scrape_config.yml
|
||||
`
|
||||
require.NoError(t, os.WriteFile(yamlFilePath, []byte(modifiedYamlContent), 0o644))
|
||||
|
||||
// Checksum should change.
|
||||
modifiedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.NotEqual(t, originalChecksum, modifiedChecksum)
|
||||
|
||||
// Revert the main YAML file.
|
||||
require.NoError(t, os.WriteFile(yamlFilePath, []byte(yamlContent), 0o644))
|
||||
|
||||
// Checksum should return to the original.
|
||||
revertedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.Equal(t, originalChecksum, revertedChecksum)
|
||||
})
|
||||
|
||||
t.Run("Rule File Removed from YAML Config", func(t *testing.T) {
|
||||
// Modify the YAML content to remove the rule file.
|
||||
modifiedYamlContent := `
|
||||
scrape_config_files:
|
||||
- scrape_config.yml
|
||||
`
|
||||
require.NoError(t, os.WriteFile(yamlFilePath, []byte(modifiedYamlContent), 0o644))
|
||||
|
||||
// Checksum should change.
|
||||
modifiedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.NotEqual(t, originalChecksum, modifiedChecksum)
|
||||
|
||||
// Revert the YAML content.
|
||||
require.NoError(t, os.WriteFile(yamlFilePath, []byte(yamlContent), 0o644))
|
||||
|
||||
// Checksum should return to the original.
|
||||
revertedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.Equal(t, originalChecksum, revertedChecksum)
|
||||
})
|
||||
|
||||
t.Run("Scrape Config Removed from YAML Config", func(t *testing.T) {
|
||||
// Modify the YAML content to remove the scrape config file.
|
||||
modifiedYamlContent := `
|
||||
rule_files:
|
||||
- rule_file.yml
|
||||
`
|
||||
require.NoError(t, os.WriteFile(yamlFilePath, []byte(modifiedYamlContent), 0o644))
|
||||
|
||||
// Checksum should change.
|
||||
modifiedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.NotEqual(t, originalChecksum, modifiedChecksum)
|
||||
|
||||
// Revert the YAML content.
|
||||
require.NoError(t, os.WriteFile(yamlFilePath, []byte(yamlContent), 0o644))
|
||||
|
||||
// Checksum should return to the original.
|
||||
revertedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.Equal(t, originalChecksum, revertedChecksum)
|
||||
})
|
||||
|
||||
t.Run("Empty Rule File", func(t *testing.T) {
|
||||
// Write an empty rule file.
|
||||
require.NoError(t, os.WriteFile(ruleFilePath, []byte(""), 0o644))
|
||||
|
||||
// Checksum should change.
|
||||
emptyChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.NotEqual(t, originalChecksum, emptyChecksum)
|
||||
|
||||
// Restore the rule file.
|
||||
require.NoError(t, os.WriteFile(ruleFilePath, []byte(originalRuleContent), 0o644))
|
||||
|
||||
// Checksum should return to the original.
|
||||
revertedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.Equal(t, originalChecksum, revertedChecksum)
|
||||
})
|
||||
|
||||
t.Run("Empty Scrape Config File", func(t *testing.T) {
|
||||
// Write an empty scrape config file.
|
||||
require.NoError(t, os.WriteFile(scrapeConfigFilePath, []byte(""), 0o644))
|
||||
|
||||
// Checksum should change.
|
||||
emptyChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.NotEqual(t, originalChecksum, emptyChecksum)
|
||||
|
||||
// Restore the scrape config file.
|
||||
require.NoError(t, os.WriteFile(scrapeConfigFilePath, []byte(originalScrapeConfigContent), 0o644))
|
||||
|
||||
// Checksum should return to the original.
|
||||
revertedChecksum := calculateChecksum(t, yamlFilePath)
|
||||
require.Equal(t, originalChecksum, revertedChecksum)
|
||||
})
|
||||
}
|
||||
|
||||
// calculateChecksum generates a checksum for the given YAML file path.
|
||||
func calculateChecksum(t *testing.T, yamlFilePath string) string {
|
||||
checksum, err := GenerateChecksum(yamlFilePath)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, checksum)
|
||||
return checksum
|
||||
}
|
6
config/testdata/conf.good.yml
vendored
6
config/testdata/conf.good.yml
vendored
|
@ -87,6 +87,12 @@ scrape_configs:
|
|||
my: label
|
||||
your: label
|
||||
|
||||
http_headers:
|
||||
foo:
|
||||
values: ["foobar"]
|
||||
secrets: ["bar", "foo"]
|
||||
files: ["valid_password_file"]
|
||||
|
||||
relabel_configs:
|
||||
- source_labels: [job, __meta_dns_name]
|
||||
regex: (.*)some-[regex]
|
||||
|
|
|
@ -394,8 +394,16 @@ func (m *Manager) updateGroup(poolKey poolKey, tgs []*targetgroup.Group) {
|
|||
m.targets[poolKey] = make(map[string]*targetgroup.Group)
|
||||
}
|
||||
for _, tg := range tgs {
|
||||
if tg != nil { // Some Discoverers send nil target group so need to check for it to avoid panics.
|
||||
// Some Discoverers send nil target group so need to check for it to avoid panics.
|
||||
if tg == nil {
|
||||
continue
|
||||
}
|
||||
if len(tg.Targets) > 0 {
|
||||
m.targets[poolKey][tg.Source] = tg
|
||||
} else {
|
||||
// The target group is empty, drop the corresponding entry to avoid leaks.
|
||||
// In case the group yielded targets before, allGroups() will take care of making consumers drop them.
|
||||
delete(m.targets[poolKey], tg.Source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1051,8 +1051,8 @@ func TestDiscovererConfigs(t *testing.T) {
|
|||
}
|
||||
|
||||
// TestTargetSetRecreatesEmptyStaticConfigs ensures that reloading a config file after
|
||||
// removing all targets from the static_configs sends an update with empty targetGroups.
|
||||
// This is required to signal the receiver that this target set has no current targets.
|
||||
// removing all targets from the static_configs cleans the corresponding targetGroups entries to avoid leaks and sends an empty update.
|
||||
// The update is required to signal the consumers that the previous targets should be dropped.
|
||||
func TestTargetSetRecreatesEmptyStaticConfigs(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
@ -1085,16 +1085,14 @@ func TestTargetSetRecreatesEmptyStaticConfigs(t *testing.T) {
|
|||
discoveryManager.ApplyConfig(c)
|
||||
|
||||
syncedTargets = <-discoveryManager.SyncCh()
|
||||
require.Len(t, discoveryManager.targets, 1)
|
||||
p = pk("static", "prometheus", 1)
|
||||
targetGroups, ok := discoveryManager.targets[p]
|
||||
require.True(t, ok, "'%v' should be present in target groups", p)
|
||||
group, ok := targetGroups[""]
|
||||
require.True(t, ok, "missing '' key in target groups %v", targetGroups)
|
||||
|
||||
require.Empty(t, group.Targets, "Invalid number of targets.")
|
||||
require.Len(t, syncedTargets, 1)
|
||||
require.Len(t, syncedTargets["prometheus"], 1)
|
||||
require.Nil(t, syncedTargets["prometheus"][0].Labels)
|
||||
require.True(t, ok, "'%v' should be present in targets", p)
|
||||
// Otherwise the targetGroups will leak, see https://github.com/prometheus/prometheus/issues/12436.
|
||||
require.Empty(t, targetGroups, 0, "'%v' should no longer have any associated target groups", p)
|
||||
require.Len(t, syncedTargets, 1, "an update with no targetGroups should still be sent.")
|
||||
require.Empty(t, syncedTargets["prometheus"], 0)
|
||||
}
|
||||
|
||||
func TestIdenticalConfigurationsAreCoalesced(t *testing.T) {
|
||||
|
|
|
@ -15,6 +15,7 @@ The Prometheus monitoring server
|
|||
| <code class="text-nowrap">-h</code>, <code class="text-nowrap">--help</code> | Show context-sensitive help (also try --help-long and --help-man). | |
|
||||
| <code class="text-nowrap">--version</code> | Show application version. | |
|
||||
| <code class="text-nowrap">--config.file</code> | Prometheus configuration file path. | `prometheus.yml` |
|
||||
| <code class="text-nowrap">--config.auto-reload-interval</code> | Specifies the interval for checking and automatically reloading the Prometheus configuration file upon detecting changes. | `30s` |
|
||||
| <code class="text-nowrap">--web.listen-address</code> <code class="text-nowrap">...<code class="text-nowrap"> | Address to listen on for UI, API, and telemetry. Can be repeated. | `0.0.0.0:9090` |
|
||||
| <code class="text-nowrap">--auto-gomemlimit.ratio</code> | The ratio of reserved GOMEMLIMIT memory to the detected maximum container or system memory | `0.9` |
|
||||
| <code class="text-nowrap">--web.config.file</code> | [EXPERIMENTAL] Path to configuration file that can enable TLS or authentication. | |
|
||||
|
@ -57,7 +58,7 @@ The Prometheus monitoring server
|
|||
| <code class="text-nowrap">--query.max-concurrency</code> | Maximum number of queries executed concurrently. Use with server mode only. | `20` |
|
||||
| <code class="text-nowrap">--query.max-samples</code> | Maximum number of samples a single query can load into memory. Note that queries will fail if they try to load more samples than this into memory, so this also limits the number of samples a query can return. Use with server mode only. | `50000000` |
|
||||
| <code class="text-nowrap">--scrape.name-escaping-scheme</code> | Method for escaping legacy invalid names when sending to Prometheus that does not support UTF-8. Can be one of "values", "underscores", or "dots". | `values` |
|
||||
| <code class="text-nowrap">--enable-feature</code> <code class="text-nowrap">...<code class="text-nowrap"> | Comma separated feature names to enable. Valid options: agent, auto-gomemlimit, exemplar-storage, expand-external-labels, memory-snapshot-on-shutdown, promql-per-step-stats, promql-experimental-functions, remote-write-receiver (DEPRECATED), extra-scrape-metrics, new-service-discovery-manager, auto-gomaxprocs, no-default-scrape-port, native-histograms, otlp-write-receiver, created-timestamp-zero-ingestion, concurrent-rule-eval, delayed-compaction, utf8-names. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details. | |
|
||||
| <code class="text-nowrap">--enable-feature</code> <code class="text-nowrap">...<code class="text-nowrap"> | Comma separated feature names to enable. Valid options: agent, auto-gomaxprocs, auto-gomemlimit, auto-reload-config, concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, expand-external-labels, extra-scrape-metrics, memory-snapshot-on-shutdown, native-histograms, new-service-discovery-manager, no-default-scrape-port, otlp-write-receiver, promql-experimental-functions, promql-delayed-name-removal, promql-per-step-stats, remote-write-receiver (DEPRECATED), utf8-names. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details. | |
|
||||
| <code class="text-nowrap">--log.level</code> | Only log messages with the given severity or above. One of: [debug, info, warn, error] | `info` |
|
||||
| <code class="text-nowrap">--log.format</code> | Output format of log messages. One of: [logfmt, json] | `logfmt` |
|
||||
|
||||
|
|
|
@ -575,7 +575,7 @@ Dump samples from a TSDB.
|
|||
|
||||
| Flag | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| <code class="text-nowrap">--sandbox-dir-root</code> | Root directory where a sandbox directory would be created in case WAL replay generates chunks. The sandbox directory is cleaned up at the end. | `data/` |
|
||||
| <code class="text-nowrap">--sandbox-dir-root</code> | Root directory where a sandbox directory will be created, this sandbox is used in case WAL replay generates chunks (default is the database path). The sandbox is cleaned up at the end. | |
|
||||
| <code class="text-nowrap">--min-time</code> | Minimum timestamp to dump. | `-9223372036854775808` |
|
||||
| <code class="text-nowrap">--max-time</code> | Maximum timestamp to dump. | `9223372036854775807` |
|
||||
| <code class="text-nowrap">--match</code> <code class="text-nowrap">...<code class="text-nowrap"> | Series selector. Can be specified multiple times. | `{__name__=~'(?s:.*)'}` |
|
||||
|
@ -602,7 +602,7 @@ Dump samples from a TSDB.
|
|||
|
||||
| Flag | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| <code class="text-nowrap">--sandbox-dir-root</code> | Root directory where a sandbox directory would be created in case WAL replay generates chunks. The sandbox directory is cleaned up at the end. | `data/` |
|
||||
| <code class="text-nowrap">--sandbox-dir-root</code> | Root directory where a sandbox directory will be created, this sandbox is used in case WAL replay generates chunks (default is the database path). The sandbox is cleaned up at the end. | |
|
||||
| <code class="text-nowrap">--min-time</code> | Minimum timestamp to dump. | `-9223372036854775808` |
|
||||
| <code class="text-nowrap">--max-time</code> | Maximum timestamp to dump. | `9223372036854775807` |
|
||||
| <code class="text-nowrap">--match</code> <code class="text-nowrap">...<code class="text-nowrap"> | Series selector. Can be specified multiple times. | `{__name__=~'(?s:.*)'}` |
|
||||
|
|
|
@ -70,7 +70,7 @@ global:
|
|||
|
||||
# How frequently to evaluate rules.
|
||||
[ evaluation_interval: <duration> | default = 1m ]
|
||||
|
||||
|
||||
# Offset the rule evaluation timestamp of this particular group by the specified duration into the past to ensure the underlying metrics have been received.
|
||||
# Metric availability delays are more likely to occur when Prometheus is running as a remote write target, but can also occur when there's anomalies with scraping.
|
||||
[ rule_query_offset: <duration> | default = 0s ]
|
||||
|
@ -307,6 +307,17 @@ tls_config:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# List of Azure service discovery configurations.
|
||||
azure_sd_configs:
|
||||
|
@ -597,6 +608,18 @@ tls_config:
|
|||
# Specifies headers to send to proxies during CONNECT requests.
|
||||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
```
|
||||
|
||||
### `<azure_sd_config>`
|
||||
|
@ -688,6 +711,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -801,6 +836,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -888,6 +935,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -946,6 +1005,18 @@ host: <string>
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# TLS configuration.
|
||||
tls_config:
|
||||
[ <tls_config> ]
|
||||
|
@ -1126,6 +1197,18 @@ host: <string>
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# TLS configuration.
|
||||
tls_config:
|
||||
[ <tls_config> ]
|
||||
|
@ -1335,6 +1418,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -1612,6 +1707,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -1838,6 +1945,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -1932,6 +2051,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -2015,6 +2146,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -2249,6 +2392,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -2340,6 +2495,18 @@ server: <string>
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# TLS configuration.
|
||||
tls_config:
|
||||
[ <tls_config> ]
|
||||
|
@ -2467,6 +2634,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -2556,6 +2735,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -2663,6 +2854,18 @@ tls_config:
|
|||
# Specifies headers to send to proxies during CONNECT requests.
|
||||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
```
|
||||
|
||||
By default every app listed in Marathon will be scraped by Prometheus. If not all
|
||||
|
@ -2762,6 +2965,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -2948,6 +3163,18 @@ tls_config:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -3074,6 +3301,18 @@ tags_filter:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# TLS configuration.
|
||||
tls_config:
|
||||
[ <tls_config> ]
|
||||
|
@ -3150,6 +3389,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -3232,6 +3483,18 @@ oauth2:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -3457,6 +3720,18 @@ tls_config:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -3720,6 +3995,18 @@ tls_config:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
@ -3841,6 +4128,18 @@ tls_config:
|
|||
[ proxy_connect_header:
|
||||
[ <string>: [<secret>, ...] ] ]
|
||||
|
||||
# Custom HTTP headers to be sent along with each request.
|
||||
# Headers that are set by Prometheus itself can't be overwritten.
|
||||
http_headers:
|
||||
# Header name.
|
||||
[ <string>:
|
||||
# Header values.
|
||||
[ values: [<string>, ...] ]
|
||||
# Headers values. Hidden in configuration page.
|
||||
[ secrets: [<secret>, ...] ]
|
||||
# Files to read header values from.
|
||||
[ files: [<string>, ...] ] ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <boolean> | default = true ]
|
||||
|
||||
|
|
|
@ -250,6 +250,14 @@ Note that during this delay, the Head continues its usual operations, which incl
|
|||
|
||||
Despite the delay in compaction, the blocks produced are time-aligned in the same manner as they would be if the delay was not in place.
|
||||
|
||||
## Delay __name__ label removal for PromQL engine
|
||||
|
||||
`--enable-feature=promql-delayed-name-removal`
|
||||
|
||||
When enabled, Prometheus will change the way in which the `__name__` label is removed from PromQL query results (for functions and expressions for which this is necessary). Specifically, it will delay the removal to the last step of the query evaluation, instead of every time an expression or function creating derived metrics is evaluated.
|
||||
|
||||
This allows optionally preserving the `__name__` label via the `label_replace` and `label_join` functions, and helps prevent the "vector cannot contain metrics with the same labelset" error, which can happen when applying a regex-matcher to the `__name__` label.
|
||||
|
||||
## UTF-8 Name Support
|
||||
|
||||
`--enable-feature=utf8-names`
|
||||
|
@ -257,3 +265,16 @@ Despite the delay in compaction, the blocks produced are time-aligned in the sam
|
|||
When enabled, changes the metric and label name validation scheme inside Prometheus to allow the full UTF-8 character set.
|
||||
By itself, this flag does not enable the request of UTF-8 names via content negotiation.
|
||||
Users will also have to set `metric_name_validation_scheme` in scrape configs to enable the feature either on the global config or on a per-scrape config basis.
|
||||
|
||||
## Auto Reload Config
|
||||
|
||||
`--enable-feature=auto-reload-config`
|
||||
|
||||
When enabled, Prometheus will automatically reload its configuration file at a
|
||||
specified interval. The interval is defined by the
|
||||
`--config.auto-reload-interval` flag, which defaults to `30s`.
|
||||
|
||||
Configuration reloads are triggered by detecting changes in the checksum of the
|
||||
main configuration file or any referenced files, such as rule and scrape
|
||||
configurations. To ensure consistency and avoid issues during reloads, it's
|
||||
recommended to update these files atomically.
|
||||
|
|
|
@ -41,7 +41,7 @@ vector is the only type which can be graphed.
|
|||
_Notes about the experimental native histograms:_
|
||||
|
||||
* Ingesting native histograms has to be enabled via a [feature
|
||||
flag](../../feature_flags.md#native-histograms).
|
||||
flag](../feature_flags.md#native-histograms).
|
||||
* Once native histograms have been ingested into the TSDB (and even after
|
||||
disabling the feature flag again), both instant vectors and range vectors may
|
||||
now contain samples that aren't simple floating point numbers (float samples)
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
module github.com/prometheus/prometheus/documentation/examples/remote_storage
|
||||
|
||||
go 1.21.0
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||
github.com/go-kit/log v0.2.1
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/snappy v0.0.4
|
||||
github.com/influxdata/influxdb v1.11.5
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/prometheus/common v0.55.0
|
||||
github.com/influxdata/influxdb v1.11.6
|
||||
github.com/prometheus/client_golang v1.20.0
|
||||
github.com/prometheus/common v0.57.0
|
||||
github.com/prometheus/prometheus v0.53.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
)
|
||||
|
@ -35,7 +35,7 @@ require (
|
|||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jpillora/backoff v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
|
@ -55,10 +55,10 @@ require (
|
|||
go.opentelemetry.io/otel/trace v1.27.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
|
||||
|
|
|
@ -166,8 +166,8 @@ github.com/hetznercloud/hcloud-go/v2 v2.9.0 h1:s0N6R7Zoi2DPfMtUF5o9VeUBzTtHVY6MI
|
|||
github.com/hetznercloud/hcloud-go/v2 v2.9.0/go.mod h1:qtW/TuU7Bs16ibXl/ktJarWqU2LwHr7eGlwoilHxtgg=
|
||||
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/influxdata/influxdb v1.11.5 h1:+em5VOl6lhAZubXj5o6SobCwvrRs3XDlBx/MUI4schI=
|
||||
github.com/influxdata/influxdb v1.11.5/go.mod h1:k8sWREQl1/9t46VrkrH5adUM4UNGIt206ipO3plbkw8=
|
||||
github.com/influxdata/influxdb v1.11.6 h1:zS5MRY+RQ5/XFTer5R8xQRnY17JYSbacvO6OaP164wU=
|
||||
github.com/influxdata/influxdb v1.11.6/go.mod h1:F10NoQb9qa04lME3pTPWQrYt4JZ/ke1Eei+1ttgHHrg=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.1.11 h1:J/uRN4UWO3wCyGOeDdMKv8LWRzKu6UIkLEaes38Kzh8=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.1.11/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
|
@ -187,8 +187,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
|
|||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
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/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
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=
|
||||
|
@ -253,8 +253,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
|||
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.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI=
|
||||
github.com/prometheus/client_golang v1.20.0/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-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
|
@ -264,8 +264,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
|||
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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/common v0.57.0 h1:Ro/rKjwdq9mZn1K5QPctzh+MA4Lp0BuYk5ZZEVhoNcY=
|
||||
github.com/prometheus/common v0.57.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI=
|
||||
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=
|
||||
|
@ -323,8 +323,8 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
|
@ -344,8 +344,8 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
|
|||
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-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
|
@ -373,11 +373,11 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.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.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/text v0.3.0/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=
|
||||
|
|
61
go.mod
61
go.mod
|
@ -1,11 +1,11 @@
|
|||
module github.com/prometheus/prometheus
|
||||
|
||||
go 1.21.0
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.22.5
|
||||
toolchain go1.23.0
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0
|
||||
|
@ -17,11 +17,11 @@ require (
|
|||
github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/dennwc/varint v1.0.0
|
||||
github.com/digitalocean/godo v1.119.0
|
||||
github.com/digitalocean/godo v1.121.0
|
||||
github.com/docker/docker v27.1.1+incompatible
|
||||
github.com/edsrzf/mmap-go v1.1.0
|
||||
github.com/envoyproxy/go-control-plane v0.12.0
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4
|
||||
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155
|
||||
github.com/envoyproxy/protoc-gen-validate v1.1.0
|
||||
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/go-kit/log v0.2.1
|
||||
|
@ -36,10 +36,10 @@ require (
|
|||
github.com/gophercloud/gophercloud v1.14.0
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
github.com/hashicorp/consul/api v1.29.2
|
||||
github.com/hashicorp/consul/api v1.29.4
|
||||
github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.12.0
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.2.0
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.13.1
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.2.1
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/klauspost/compress v1.17.9
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b
|
||||
|
@ -52,9 +52,9 @@ require (
|
|||
github.com/oklog/ulid v1.3.1
|
||||
github.com/ovh/go-ovh v1.6.0
|
||||
github.com/prometheus/alertmanager v0.27.0
|
||||
github.com/prometheus/client_golang v1.20.0
|
||||
github.com/prometheus/client_golang v1.20.3
|
||||
github.com/prometheus/client_model v0.6.1
|
||||
github.com/prometheus/common v0.56.0
|
||||
github.com/prometheus/common v0.58.0
|
||||
github.com/prometheus/common/assets v0.2.0
|
||||
github.com/prometheus/common/sigv4 v0.1.0
|
||||
github.com/prometheus/exporter-toolkit v0.11.0
|
||||
|
@ -64,26 +64,26 @@ require (
|
|||
github.com/vultr/govultr/v2 v2.17.2
|
||||
go.opentelemetry.io/collector/pdata v1.12.0
|
||||
go.opentelemetry.io/collector/semconv v0.105.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0
|
||||
go.opentelemetry.io/otel v1.28.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0
|
||||
go.opentelemetry.io/otel v1.29.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0
|
||||
go.opentelemetry.io/otel/sdk v1.28.0
|
||||
go.opentelemetry.io/otel/trace v1.28.0
|
||||
go.opentelemetry.io/otel/trace v1.29.0
|
||||
go.uber.org/atomic v1.11.0
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
go.uber.org/goleak v1.3.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/oauth2 v0.21.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/sys v0.22.0
|
||||
golang.org/x/text v0.16.0
|
||||
golang.org/x/time v0.5.0
|
||||
golang.org/x/oauth2 v0.22.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sys v0.25.0
|
||||
golang.org/x/text v0.17.0
|
||||
golang.org/x/time v0.6.0
|
||||
golang.org/x/tools v0.23.0
|
||||
google.golang.org/api v0.190.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/api v0.196.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed
|
||||
google.golang.org/grpc v1.66.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
|
@ -95,8 +95,8 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/auth v0.7.3 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect
|
||||
cloud.google.com/go/auth v0.9.3 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
|
@ -140,7 +140,7 @@ require (
|
|||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/s2a-go v0.1.8 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||
|
@ -176,6 +176,7 @@ require (
|
|||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
|
@ -184,14 +185,14 @@ require (
|
|||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/term v0.22.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gotest.tools/v3 v3.0.3 // indirect
|
||||
|
|
113
go.sum
113
go.sum
|
@ -12,10 +12,10 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
|
|||
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.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go/auth v0.7.3 h1:98Vr+5jMaCZ5NZk6e/uBgf60phTk/XN84r8QEWB9yjY=
|
||||
cloud.google.com/go/auth v0.7.3/go.mod h1:HJtWUx1P5eqjy/f6Iq5KeytNpbAcGolPhOgyop2LlzA=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
|
||||
cloud.google.com/go/auth v0.9.3 h1:VOEUIAADkkLtyfr3BLa3R8Ed/j6w1jTBmARx+wb5w5U=
|
||||
cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
|
||||
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=
|
||||
|
@ -36,8 +36,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
|||
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/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 h1:GJHeeA2N7xrG3q30L2UXDyuWRzDM900/65j70wcM4Ww=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
|
||||
|
@ -143,8 +143,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
|
|||
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/digitalocean/godo v1.119.0 h1:dmFNQwSIAcH3z+FVovHLkazKDC2uA8oOlGvg5+H4vRw=
|
||||
github.com/digitalocean/godo v1.119.0/go.mod h1:WQVH83OHUy6gC4gXpEVQKtxTd4L5oCp+5OialidkPLY=
|
||||
github.com/digitalocean/godo v1.121.0 h1:ilXiHuEnhbJs2fmFEPX0r/QQ6KfiOIMAhJN3f8NiCfI=
|
||||
github.com/digitalocean/godo v1.121.0/go.mod h1:WQVH83OHUy6gC4gXpEVQKtxTd4L5oCp+5OialidkPLY=
|
||||
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
||||
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
|
@ -168,11 +168,11 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s
|
|||
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.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI=
|
||||
github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0=
|
||||
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc=
|
||||
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
||||
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 v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM=
|
||||
|
@ -328,8 +328,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||
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/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.3 h1:QRje2j5GZimBzlbhGA2V2QlGNgL8G6e+wGo/+/2bWI0=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.3/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||
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.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
||||
|
@ -353,8 +353,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
|
|||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||
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/api v1.29.4 h1:P6slzxDLBOxUSj3fWo2o65VuKtbtOXFi7TSSgtXutuE=
|
||||
github.com/hashicorp/consul/api v1.29.4/go.mod h1:HUlfw+l2Zy68ceJavv2zAyArl2fqhGWnMycyt56sBgg=
|
||||
github.com/hashicorp/consul/proto-public v0.6.2 h1:+DA/3g/IiKlJZb88NBn0ZgXrxJp2NlvCZdEyl+qxvL0=
|
||||
github.com/hashicorp/consul/proto-public v0.6.2/go.mod h1:cXXbOg74KBNGajC+o8RlA502Esf0R9prcoJgiOX/2Tg=
|
||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
|
@ -414,8 +414,8 @@ github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3/go.mod h1:svtx
|
|||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
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/v2 v2.12.0 h1:nOgfNTo0gyXZJJdM8mo/XH5MO/e80wAEpldRzdWayhY=
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.12.0/go.mod h1:dhix40Br3fDiBhwaSG/zgaYOFFddpfBm/6R1Zz0IiF0=
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.13.1 h1:jq0GP4QaYE5d8xR/Zw17s9qoaESRJMXfGmtD1a/qckQ=
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.13.1/go.mod h1:dhix40Br3fDiBhwaSG/zgaYOFFddpfBm/6R1Zz0IiF0=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
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=
|
||||
|
@ -423,8 +423,8 @@ 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/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.2.0 h1:qX7gachC0wJSmFfVRnd+DHmz9AStvVraKcwQ/JokIB4=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.2.0/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.2.1 h1:mxxN+frNVmbFrmmFfXnBC3g2USYJrl6mc1LW2iNYbFY=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.2.1/go.mod h1:SXrO9OGyWjd2rZhAhEpdYN6VUAODzzqRdqA9BCviQtI=
|
||||
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
|
||||
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
|
@ -592,6 +592,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
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 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=
|
||||
|
@ -608,8 +610,8 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD
|
|||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
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.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI=
|
||||
github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4=
|
||||
github.com/prometheus/client_golang v1.20.3/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=
|
||||
|
@ -625,8 +627,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
|
|||
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.56.0 h1:UffReloqkBtvtQEYDg2s+uDPGRrJyC6vZWPGXf6OhPY=
|
||||
github.com/prometheus/common v0.56.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI=
|
||||
github.com/prometheus/common v0.58.0 h1:N+N8vY4/23r6iYfD3UQZUoJPnUYAo7v6LG5XZxjZTXo=
|
||||
github.com/prometheus/common v0.58.0/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
|
||||
github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM=
|
||||
github.com/prometheus/common/assets v0.2.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI=
|
||||
github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4=
|
||||
|
@ -728,22 +730,22 @@ go.opentelemetry.io/collector/pdata v1.12.0 h1:Xx5VK1p4VO0md8MWm2icwC1MnJ7f8EimK
|
|||
go.opentelemetry.io/collector/pdata v1.12.0/go.mod h1:MYeB0MmMAxeM0hstCFrCqWLzdyeYySim2dG6pDT6nYI=
|
||||
go.opentelemetry.io/collector/semconv v0.105.0 h1:8p6dZ3JfxFTjbY38d8xlQGB1TQ3nPUvs+D0RERniZ1g=
|
||||
go.opentelemetry.io/collector/semconv v0.105.0/go.mod h1:yMVUCNoQPZVq/IPfrHrnntZTWsLf5YGZ7qwKulIl5hw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
|
@ -774,8 +776,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
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=
|
||||
|
@ -857,16 +859,16 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
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.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
|
||||
golang.org/x/oauth2 v0.22.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=
|
||||
|
@ -880,8 +882,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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=
|
||||
|
@ -947,16 +949,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
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=
|
||||
|
@ -968,14 +970,15 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
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.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.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=
|
||||
|
@ -1047,8 +1050,8 @@ google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
|
|||
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.190.0 h1:ASM+IhLY1zljNdLu19W1jTmU6A+gMk6M46Wlur61s+Q=
|
||||
google.golang.org/api v0.190.0/go.mod h1:QIr6I9iedBLnfqoD6L6Vze1UvS5Hzj5r2aUBOaZnLHo=
|
||||
google.golang.org/api v0.196.0 h1:k/RafYqebaIJBO3+SMnfEGtFVlvp5vSgqTUF54UN/zg=
|
||||
google.golang.org/api v0.196.0/go.mod h1:g9IL21uGkYgvQ5BZg6BAtoGJQIm8r6EgaAbpNey5wBE=
|
||||
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=
|
||||
|
@ -1085,10 +1088,10 @@ google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1m
|
|||
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-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f h1:b1Ln/PG8orm0SsBbHZWke8dDp2lrCD4jSmfglFpTZbk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
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=
|
||||
|
@ -1107,8 +1110,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
|
|||
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.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
|
||||
google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
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=
|
||||
|
|
|
@ -409,6 +409,7 @@ func (p *ProtobufParser) Next() (Entry, error) {
|
|||
switch p.state {
|
||||
case EntryInvalid:
|
||||
p.metricPos = 0
|
||||
p.exemplarPos = 0
|
||||
p.fieldPos = -2
|
||||
n, err := readDelimited(p.in[p.inPos:], p.mf)
|
||||
p.inPos += n
|
||||
|
@ -485,6 +486,7 @@ func (p *ProtobufParser) Next() (Entry, error) {
|
|||
p.metricPos++
|
||||
p.fieldPos = -2
|
||||
p.fieldsDone = false
|
||||
p.exemplarPos = 0
|
||||
// If this is a metric family containing native
|
||||
// histograms, we have to switch back to native
|
||||
// histograms after parsing a classic histogram.
|
||||
|
|
|
@ -695,6 +695,70 @@ metric: <
|
|||
timestamp_ms: 1234568
|
||||
>
|
||||
|
||||
`,
|
||||
|
||||
`name: "test_histogram_with_native_histogram_exemplars2"
|
||||
help: "Another histogram with native histogram exemplars."
|
||||
type: HISTOGRAM
|
||||
metric: <
|
||||
histogram: <
|
||||
sample_count: 175
|
||||
sample_sum: 0.0008280461746287094
|
||||
bucket: <
|
||||
cumulative_count: 2
|
||||
upper_bound: -0.0004899999999999998
|
||||
>
|
||||
bucket: <
|
||||
cumulative_count: 4
|
||||
upper_bound: -0.0003899999999999998
|
||||
>
|
||||
bucket: <
|
||||
cumulative_count: 16
|
||||
upper_bound: -0.0002899999999999998
|
||||
>
|
||||
schema: 3
|
||||
zero_threshold: 2.938735877055719e-39
|
||||
zero_count: 2
|
||||
negative_span: <
|
||||
offset: -162
|
||||
length: 1
|
||||
>
|
||||
negative_span: <
|
||||
offset: 23
|
||||
length: 4
|
||||
>
|
||||
negative_delta: 1
|
||||
negative_delta: 3
|
||||
negative_delta: -2
|
||||
negative_delta: -1
|
||||
negative_delta: 1
|
||||
positive_span: <
|
||||
offset: -161
|
||||
length: 1
|
||||
>
|
||||
positive_span: <
|
||||
offset: 8
|
||||
length: 3
|
||||
>
|
||||
positive_delta: 1
|
||||
positive_delta: 2
|
||||
positive_delta: -1
|
||||
positive_delta: -1
|
||||
exemplars: <
|
||||
label: <
|
||||
name: "dummyID"
|
||||
value: "59780"
|
||||
>
|
||||
value: -0.00039
|
||||
timestamp: <
|
||||
seconds: 1625851155
|
||||
nanos: 146848499
|
||||
>
|
||||
>
|
||||
>
|
||||
timestamp_ms: 1234568
|
||||
>
|
||||
|
||||
`,
|
||||
}
|
||||
|
||||
|
@ -1276,6 +1340,41 @@ func TestProtobufParse(t *testing.T) {
|
|||
{Labels: labels.FromStrings("dummyID", "59772"), Value: -0.00052, HasTs: true, Ts: 1625851160156},
|
||||
},
|
||||
},
|
||||
{
|
||||
m: "test_histogram_with_native_histogram_exemplars2",
|
||||
help: "Another histogram with native histogram exemplars.",
|
||||
},
|
||||
{
|
||||
m: "test_histogram_with_native_histogram_exemplars2",
|
||||
typ: model.MetricTypeHistogram,
|
||||
},
|
||||
{
|
||||
m: "test_histogram_with_native_histogram_exemplars2",
|
||||
t: 1234568,
|
||||
shs: &histogram.Histogram{
|
||||
Count: 175,
|
||||
ZeroCount: 2,
|
||||
Sum: 0.0008280461746287094,
|
||||
ZeroThreshold: 2.938735877055719e-39,
|
||||
Schema: 3,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: -161, Length: 1},
|
||||
{Offset: 8, Length: 3},
|
||||
},
|
||||
NegativeSpans: []histogram.Span{
|
||||
{Offset: -162, Length: 1},
|
||||
{Offset: 23, Length: 4},
|
||||
},
|
||||
PositiveBuckets: []int64{1, 2, -1, -1},
|
||||
NegativeBuckets: []int64{1, 3, -2, -1, 1},
|
||||
},
|
||||
lset: labels.FromStrings(
|
||||
"__name__", "test_histogram_with_native_histogram_exemplars2",
|
||||
),
|
||||
e: []exemplar.Exemplar{
|
||||
{Labels: labels.FromStrings("dummyID", "59780"), Value: -0.00039, HasTs: true, Ts: 1625851155146},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1995,15 +2094,15 @@ func TestProtobufParse(t *testing.T) {
|
|||
"__name__", "without_quantiles_sum",
|
||||
),
|
||||
},
|
||||
{ // 78
|
||||
{ // 81
|
||||
m: "empty_histogram",
|
||||
help: "A histogram without observations and with a zero threshold of zero but with a no-op span to identify it as a native histogram.",
|
||||
},
|
||||
{ // 79
|
||||
{ // 82
|
||||
m: "empty_histogram",
|
||||
typ: model.MetricTypeHistogram,
|
||||
},
|
||||
{ // 80
|
||||
{ // 83
|
||||
m: "empty_histogram",
|
||||
shs: &histogram.Histogram{
|
||||
CounterResetHint: histogram.UnknownCounterReset,
|
||||
|
@ -2014,15 +2113,15 @@ func TestProtobufParse(t *testing.T) {
|
|||
"__name__", "empty_histogram",
|
||||
),
|
||||
},
|
||||
{ // 81
|
||||
{ // 84
|
||||
m: "test_counter_with_createdtimestamp",
|
||||
help: "A counter with a created timestamp.",
|
||||
},
|
||||
{ // 82
|
||||
{ // 85
|
||||
m: "test_counter_with_createdtimestamp",
|
||||
typ: model.MetricTypeCounter,
|
||||
},
|
||||
{ // 83
|
||||
{ // 86
|
||||
m: "test_counter_with_createdtimestamp",
|
||||
v: 42,
|
||||
ct: 1000,
|
||||
|
@ -2030,15 +2129,15 @@ func TestProtobufParse(t *testing.T) {
|
|||
"__name__", "test_counter_with_createdtimestamp",
|
||||
),
|
||||
},
|
||||
{ // 84
|
||||
{ // 87
|
||||
m: "test_summary_with_createdtimestamp",
|
||||
help: "A summary with a created timestamp.",
|
||||
},
|
||||
{ // 85
|
||||
{ // 88
|
||||
m: "test_summary_with_createdtimestamp",
|
||||
typ: model.MetricTypeSummary,
|
||||
},
|
||||
{ // 86
|
||||
{ // 89
|
||||
m: "test_summary_with_createdtimestamp_count",
|
||||
v: 42,
|
||||
ct: 1000,
|
||||
|
@ -2046,7 +2145,7 @@ func TestProtobufParse(t *testing.T) {
|
|||
"__name__", "test_summary_with_createdtimestamp_count",
|
||||
),
|
||||
},
|
||||
{ // 87
|
||||
{ // 90
|
||||
m: "test_summary_with_createdtimestamp_sum",
|
||||
v: 1.234,
|
||||
ct: 1000,
|
||||
|
@ -2054,15 +2153,15 @@ func TestProtobufParse(t *testing.T) {
|
|||
"__name__", "test_summary_with_createdtimestamp_sum",
|
||||
),
|
||||
},
|
||||
{ // 88
|
||||
{ // 91
|
||||
m: "test_histogram_with_createdtimestamp",
|
||||
help: "A histogram with a created timestamp.",
|
||||
},
|
||||
{ // 89
|
||||
{ // 92
|
||||
m: "test_histogram_with_createdtimestamp",
|
||||
typ: model.MetricTypeHistogram,
|
||||
},
|
||||
{ // 90
|
||||
{ // 93
|
||||
m: "test_histogram_with_createdtimestamp",
|
||||
ct: 1000,
|
||||
shs: &histogram.Histogram{
|
||||
|
@ -2074,15 +2173,15 @@ func TestProtobufParse(t *testing.T) {
|
|||
"__name__", "test_histogram_with_createdtimestamp",
|
||||
),
|
||||
},
|
||||
{ // 91
|
||||
{ // 94
|
||||
m: "test_gaugehistogram_with_createdtimestamp",
|
||||
help: "A gauge histogram with a created timestamp.",
|
||||
},
|
||||
{ // 92
|
||||
{ // 95
|
||||
m: "test_gaugehistogram_with_createdtimestamp",
|
||||
typ: model.MetricTypeGaugeHistogram,
|
||||
},
|
||||
{ // 93
|
||||
{ // 96
|
||||
m: "test_gaugehistogram_with_createdtimestamp",
|
||||
ct: 1000,
|
||||
shs: &histogram.Histogram{
|
||||
|
@ -2094,15 +2193,15 @@ func TestProtobufParse(t *testing.T) {
|
|||
"__name__", "test_gaugehistogram_with_createdtimestamp",
|
||||
),
|
||||
},
|
||||
{ // 94
|
||||
{ // 97
|
||||
m: "test_histogram_with_native_histogram_exemplars",
|
||||
help: "A histogram with native histogram exemplars.",
|
||||
},
|
||||
{ // 95
|
||||
{ // 98
|
||||
m: "test_histogram_with_native_histogram_exemplars",
|
||||
typ: model.MetricTypeHistogram,
|
||||
},
|
||||
{ // 96
|
||||
{ // 99
|
||||
m: "test_histogram_with_native_histogram_exemplars",
|
||||
t: 1234568,
|
||||
shs: &histogram.Histogram{
|
||||
|
@ -2130,7 +2229,7 @@ func TestProtobufParse(t *testing.T) {
|
|||
{Labels: labels.FromStrings("dummyID", "59772"), Value: -0.00052, HasTs: true, Ts: 1625851160156},
|
||||
},
|
||||
},
|
||||
{ // 97
|
||||
{ // 100
|
||||
m: "test_histogram_with_native_histogram_exemplars_count",
|
||||
t: 1234568,
|
||||
v: 175,
|
||||
|
@ -2138,7 +2237,7 @@ func TestProtobufParse(t *testing.T) {
|
|||
"__name__", "test_histogram_with_native_histogram_exemplars_count",
|
||||
),
|
||||
},
|
||||
{ // 98
|
||||
{ // 101
|
||||
m: "test_histogram_with_native_histogram_exemplars_sum",
|
||||
t: 1234568,
|
||||
v: 0.0008280461746287094,
|
||||
|
@ -2146,7 +2245,7 @@ func TestProtobufParse(t *testing.T) {
|
|||
"__name__", "test_histogram_with_native_histogram_exemplars_sum",
|
||||
),
|
||||
},
|
||||
{ // 99
|
||||
{ // 102
|
||||
m: "test_histogram_with_native_histogram_exemplars_bucket\xffle\xff-0.0004899999999999998",
|
||||
t: 1234568,
|
||||
v: 2,
|
||||
|
@ -2155,7 +2254,7 @@ func TestProtobufParse(t *testing.T) {
|
|||
"le", "-0.0004899999999999998",
|
||||
),
|
||||
},
|
||||
{ // 100
|
||||
{ // 103
|
||||
m: "test_histogram_with_native_histogram_exemplars_bucket\xffle\xff-0.0003899999999999998",
|
||||
t: 1234568,
|
||||
v: 4,
|
||||
|
@ -2167,7 +2266,7 @@ func TestProtobufParse(t *testing.T) {
|
|||
{Labels: labels.FromStrings("dummyID", "59727"), Value: -0.00039, HasTs: true, Ts: 1625851155146},
|
||||
},
|
||||
},
|
||||
{ // 101
|
||||
{ // 104
|
||||
m: "test_histogram_with_native_histogram_exemplars_bucket\xffle\xff-0.0002899999999999998",
|
||||
t: 1234568,
|
||||
v: 16,
|
||||
|
@ -2179,7 +2278,7 @@ func TestProtobufParse(t *testing.T) {
|
|||
{Labels: labels.FromStrings("dummyID", "5617"), Value: -0.00029, HasTs: false},
|
||||
},
|
||||
},
|
||||
{ // 102
|
||||
{ // 105
|
||||
m: "test_histogram_with_native_histogram_exemplars_bucket\xffle\xff+Inf",
|
||||
t: 1234568,
|
||||
v: 175,
|
||||
|
@ -2188,6 +2287,93 @@ func TestProtobufParse(t *testing.T) {
|
|||
"le", "+Inf",
|
||||
),
|
||||
},
|
||||
{ // 106
|
||||
m: "test_histogram_with_native_histogram_exemplars2",
|
||||
help: "Another histogram with native histogram exemplars.",
|
||||
},
|
||||
{ // 107
|
||||
m: "test_histogram_with_native_histogram_exemplars2",
|
||||
typ: model.MetricTypeHistogram,
|
||||
},
|
||||
{ // 108
|
||||
m: "test_histogram_with_native_histogram_exemplars2",
|
||||
t: 1234568,
|
||||
shs: &histogram.Histogram{
|
||||
Count: 175,
|
||||
ZeroCount: 2,
|
||||
Sum: 0.0008280461746287094,
|
||||
ZeroThreshold: 2.938735877055719e-39,
|
||||
Schema: 3,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: -161, Length: 1},
|
||||
{Offset: 8, Length: 3},
|
||||
},
|
||||
NegativeSpans: []histogram.Span{
|
||||
{Offset: -162, Length: 1},
|
||||
{Offset: 23, Length: 4},
|
||||
},
|
||||
PositiveBuckets: []int64{1, 2, -1, -1},
|
||||
NegativeBuckets: []int64{1, 3, -2, -1, 1},
|
||||
},
|
||||
lset: labels.FromStrings(
|
||||
"__name__", "test_histogram_with_native_histogram_exemplars2",
|
||||
),
|
||||
e: []exemplar.Exemplar{
|
||||
{Labels: labels.FromStrings("dummyID", "59780"), Value: -0.00039, HasTs: true, Ts: 1625851155146},
|
||||
},
|
||||
},
|
||||
{ // 109
|
||||
m: "test_histogram_with_native_histogram_exemplars2_count",
|
||||
t: 1234568,
|
||||
v: 175,
|
||||
lset: labels.FromStrings(
|
||||
"__name__", "test_histogram_with_native_histogram_exemplars2_count",
|
||||
),
|
||||
},
|
||||
{ // 110
|
||||
m: "test_histogram_with_native_histogram_exemplars2_sum",
|
||||
t: 1234568,
|
||||
v: 0.0008280461746287094,
|
||||
lset: labels.FromStrings(
|
||||
"__name__", "test_histogram_with_native_histogram_exemplars2_sum",
|
||||
),
|
||||
},
|
||||
{ // 111
|
||||
m: "test_histogram_with_native_histogram_exemplars2_bucket\xffle\xff-0.0004899999999999998",
|
||||
t: 1234568,
|
||||
v: 2,
|
||||
lset: labels.FromStrings(
|
||||
"__name__", "test_histogram_with_native_histogram_exemplars2_bucket",
|
||||
"le", "-0.0004899999999999998",
|
||||
),
|
||||
},
|
||||
{ // 112
|
||||
m: "test_histogram_with_native_histogram_exemplars2_bucket\xffle\xff-0.0003899999999999998",
|
||||
t: 1234568,
|
||||
v: 4,
|
||||
lset: labels.FromStrings(
|
||||
"__name__", "test_histogram_with_native_histogram_exemplars2_bucket",
|
||||
"le", "-0.0003899999999999998",
|
||||
),
|
||||
},
|
||||
{ // 113
|
||||
m: "test_histogram_with_native_histogram_exemplars2_bucket\xffle\xff-0.0002899999999999998",
|
||||
t: 1234568,
|
||||
v: 16,
|
||||
lset: labels.FromStrings(
|
||||
"__name__", "test_histogram_with_native_histogram_exemplars2_bucket",
|
||||
"le", "-0.0002899999999999998",
|
||||
),
|
||||
},
|
||||
{ // 114
|
||||
m: "test_histogram_with_native_histogram_exemplars2_bucket\xffle\xff+Inf",
|
||||
t: 1234568,
|
||||
v: 175,
|
||||
lset: labels.FromStrings(
|
||||
"__name__", "test_histogram_with_native_histogram_exemplars2_bucket",
|
||||
"le", "+Inf",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
"github.com/prometheus/prometheus/promql/promqltest"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/tsdb/tsdbutil"
|
||||
"github.com/prometheus/prometheus/util/teststorage"
|
||||
|
@ -274,7 +275,7 @@ func BenchmarkRangeQuery(b *testing.B) {
|
|||
MaxSamples: 50000000,
|
||||
Timeout: 100 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(b, opts)
|
||||
|
||||
const interval = 10000 // 10s interval.
|
||||
// A day of data plus 10k steps.
|
||||
|
@ -365,7 +366,7 @@ func BenchmarkNativeHistograms(b *testing.B) {
|
|||
|
||||
for _, tc := range cases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
ng := promql.NewEngine(opts)
|
||||
ng := promqltest.NewTestEngineWithOpts(b, opts)
|
||||
for i := 0; i < b.N; i++ {
|
||||
qry, err := ng.NewRangeQuery(context.Background(), testStorage, nil, tc.query, start, end, step)
|
||||
if err != nil {
|
||||
|
|
246
promql/engine.go
246
promql/engine.go
|
@ -19,6 +19,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
@ -271,6 +272,8 @@ func contextErr(err error, env string) error {
|
|||
//
|
||||
// 2) Enforcement of the maximum number of concurrent queries.
|
||||
type QueryTracker interface {
|
||||
io.Closer
|
||||
|
||||
// GetMaxConcurrent returns maximum number of concurrent queries that are allowed by this tracker.
|
||||
GetMaxConcurrent() int
|
||||
|
||||
|
@ -313,6 +316,11 @@ type EngineOpts struct {
|
|||
|
||||
// EnablePerStepStats if true allows for per-step stats to be computed on request. Disabled otherwise.
|
||||
EnablePerStepStats bool
|
||||
|
||||
// EnableDelayedNameRemoval delays the removal of the __name__ label to the last step of the query evaluation.
|
||||
// This is useful in certain scenarios where the __name__ label must be preserved or where applying a
|
||||
// regex-matcher to the __name__ label may otherwise lead to duplicate labelset errors.
|
||||
EnableDelayedNameRemoval bool
|
||||
}
|
||||
|
||||
// Engine handles the lifetime of queries from beginning to end.
|
||||
|
@ -330,6 +338,7 @@ type Engine struct {
|
|||
enableAtModifier bool
|
||||
enableNegativeOffset bool
|
||||
enablePerStepStats bool
|
||||
enableDelayedNameRemoval bool
|
||||
}
|
||||
|
||||
// NewEngine returns a new engine.
|
||||
|
@ -420,9 +429,18 @@ func NewEngine(opts EngineOpts) *Engine {
|
|||
enableAtModifier: opts.EnableAtModifier,
|
||||
enableNegativeOffset: opts.EnableNegativeOffset,
|
||||
enablePerStepStats: opts.EnablePerStepStats,
|
||||
enableDelayedNameRemoval: opts.EnableDelayedNameRemoval,
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes ng.
|
||||
func (ng *Engine) Close() error {
|
||||
if ng.activeQueryTracker != nil {
|
||||
return ng.activeQueryTracker.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetQueryLogger sets the query logger.
|
||||
func (ng *Engine) SetQueryLogger(l QueryLogger) {
|
||||
ng.queryLoggerLock.Lock()
|
||||
|
@ -706,16 +724,16 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.Eval
|
|||
startTimestamp: start,
|
||||
endTimestamp: start,
|
||||
interval: 1,
|
||||
ctx: ctxInnerEval,
|
||||
maxSamples: ng.maxSamplesPerQuery,
|
||||
logger: ng.logger,
|
||||
lookbackDelta: s.LookbackDelta,
|
||||
samplesStats: query.sampleStats,
|
||||
noStepSubqueryIntervalFn: ng.noStepSubqueryIntervalFn,
|
||||
enableDelayedNameRemoval: ng.enableDelayedNameRemoval,
|
||||
}
|
||||
query.sampleStats.InitStepTracking(start, start, 1)
|
||||
|
||||
val, warnings, err := evaluator.Eval(s.Expr)
|
||||
val, warnings, err := evaluator.Eval(ctxInnerEval, s.Expr)
|
||||
|
||||
evalSpanTimer.Finish()
|
||||
|
||||
|
@ -743,9 +761,9 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.Eval
|
|||
// Point might have a different timestamp, force it to the evaluation
|
||||
// timestamp as that is when we ran the evaluation.
|
||||
if len(s.Histograms) > 0 {
|
||||
vector[i] = Sample{Metric: s.Metric, H: s.Histograms[0].H, T: start}
|
||||
vector[i] = Sample{Metric: s.Metric, H: s.Histograms[0].H, T: start, DropName: s.DropName}
|
||||
} else {
|
||||
vector[i] = Sample{Metric: s.Metric, F: s.Floats[0].F, T: start}
|
||||
vector[i] = Sample{Metric: s.Metric, F: s.Floats[0].F, T: start, DropName: s.DropName}
|
||||
}
|
||||
}
|
||||
return vector, warnings, nil
|
||||
|
@ -764,15 +782,15 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.Eval
|
|||
startTimestamp: timeMilliseconds(s.Start),
|
||||
endTimestamp: timeMilliseconds(s.End),
|
||||
interval: durationMilliseconds(s.Interval),
|
||||
ctx: ctxInnerEval,
|
||||
maxSamples: ng.maxSamplesPerQuery,
|
||||
logger: ng.logger,
|
||||
lookbackDelta: s.LookbackDelta,
|
||||
samplesStats: query.sampleStats,
|
||||
noStepSubqueryIntervalFn: ng.noStepSubqueryIntervalFn,
|
||||
enableDelayedNameRemoval: ng.enableDelayedNameRemoval,
|
||||
}
|
||||
query.sampleStats.InitStepTracking(evaluator.startTimestamp, evaluator.endTimestamp, evaluator.interval)
|
||||
val, warnings, err := evaluator.Eval(s.Expr)
|
||||
val, warnings, err := evaluator.Eval(ctxInnerEval, s.Expr)
|
||||
|
||||
evalSpanTimer.Finish()
|
||||
|
||||
|
@ -984,6 +1002,8 @@ func checkAndExpandSeriesSet(ctx context.Context, expr parser.Expr) (annotations
|
|||
if e.Series != nil {
|
||||
return nil, nil
|
||||
}
|
||||
span := trace.SpanFromContext(ctx)
|
||||
span.AddEvent("expand start", trace.WithAttributes(attribute.String("selector", e.String())))
|
||||
series, ws, err := expandSeriesSet(ctx, e.UnexpandedSeriesSet)
|
||||
if e.SkipHistogramBuckets {
|
||||
for i := range series {
|
||||
|
@ -991,6 +1011,7 @@ func checkAndExpandSeriesSet(ctx context.Context, expr parser.Expr) (annotations
|
|||
}
|
||||
}
|
||||
e.Series = series
|
||||
span.AddEvent("expand end", trace.WithAttributes(attribute.Int("num_series", len(series))))
|
||||
return ws, err
|
||||
}
|
||||
return nil, nil
|
||||
|
@ -1020,8 +1041,6 @@ func (e errWithWarnings) Error() string { return e.err.Error() }
|
|||
// querier and reports errors. On timeout or cancellation of its context it
|
||||
// terminates.
|
||||
type evaluator struct {
|
||||
ctx context.Context
|
||||
|
||||
startTimestamp int64 // Start time in milliseconds.
|
||||
endTimestamp int64 // End time in milliseconds.
|
||||
interval int64 // Interval in milliseconds.
|
||||
|
@ -1032,6 +1051,7 @@ type evaluator struct {
|
|||
lookbackDelta time.Duration
|
||||
samplesStats *stats.QuerySamples
|
||||
noStepSubqueryIntervalFn func(rangeMillis int64) int64
|
||||
enableDelayedNameRemoval bool
|
||||
}
|
||||
|
||||
// errorf causes a panic with the input formatted into an error.
|
||||
|
@ -1069,10 +1089,13 @@ func (ev *evaluator) recover(expr parser.Expr, ws *annotations.Annotations, errp
|
|||
}
|
||||
}
|
||||
|
||||
func (ev *evaluator) Eval(expr parser.Expr) (v parser.Value, ws annotations.Annotations, err error) {
|
||||
func (ev *evaluator) Eval(ctx context.Context, expr parser.Expr) (v parser.Value, ws annotations.Annotations, err error) {
|
||||
defer ev.recover(expr, &ws, &err)
|
||||
|
||||
v, ws = ev.eval(expr)
|
||||
v, ws = ev.eval(ctx, expr)
|
||||
if ev.enableDelayedNameRemoval {
|
||||
ev.cleanupMetricLabels(v)
|
||||
}
|
||||
return v, ws, nil
|
||||
}
|
||||
|
||||
|
@ -1101,6 +1124,9 @@ type EvalNodeHelper struct {
|
|||
rightSigs map[string]Sample
|
||||
matchedSigs map[string]map[uint64]struct{}
|
||||
resultMetric map[string]labels.Labels
|
||||
|
||||
// Additional options for the evaluation.
|
||||
enableDelayedNameRemoval bool
|
||||
}
|
||||
|
||||
func (enh *EvalNodeHelper) resetBuilder(lbls labels.Labels) {
|
||||
|
@ -1117,7 +1143,7 @@ func (enh *EvalNodeHelper) resetBuilder(lbls labels.Labels) {
|
|||
// function call results.
|
||||
// The prepSeries function (if provided) can be used to prepare the helper
|
||||
// for each series, then passed to each call funcCall.
|
||||
func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper), funcCall func([]parser.Value, [][]EvalSeriesHelper, *EvalNodeHelper) (Vector, annotations.Annotations), exprs ...parser.Expr) (Matrix, annotations.Annotations) {
|
||||
func (ev *evaluator) rangeEval(ctx context.Context, prepSeries func(labels.Labels, *EvalSeriesHelper), funcCall func([]parser.Value, [][]EvalSeriesHelper, *EvalNodeHelper) (Vector, annotations.Annotations), exprs ...parser.Expr) (Matrix, annotations.Annotations) {
|
||||
numSteps := int((ev.endTimestamp-ev.startTimestamp)/ev.interval) + 1
|
||||
matrixes := make([]Matrix, len(exprs))
|
||||
origMatrixes := make([]Matrix, len(exprs))
|
||||
|
@ -1128,7 +1154,7 @@ func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper)
|
|||
// Functions will take string arguments from the expressions, not the values.
|
||||
if e != nil && e.Type() != parser.ValueTypeString {
|
||||
// ev.currentSamples will be updated to the correct value within the ev.eval call.
|
||||
val, ws := ev.eval(e)
|
||||
val, ws := ev.eval(ctx, e)
|
||||
warnings.Merge(ws)
|
||||
matrixes[i] = val.(Matrix)
|
||||
|
||||
|
@ -1150,7 +1176,7 @@ func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper)
|
|||
biggestLen = len(matrixes[i])
|
||||
}
|
||||
}
|
||||
enh := &EvalNodeHelper{Out: make(Vector, 0, biggestLen)}
|
||||
enh := &EvalNodeHelper{Out: make(Vector, 0, biggestLen), enableDelayedNameRemoval: ev.enableDelayedNameRemoval}
|
||||
type seriesAndTimestamp struct {
|
||||
Series
|
||||
ts int64
|
||||
|
@ -1180,7 +1206,7 @@ func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper)
|
|||
}
|
||||
|
||||
for ts := ev.startTimestamp; ts <= ev.endTimestamp; ts += ev.interval {
|
||||
if err := contextDone(ev.ctx, "expression evaluation"); err != nil {
|
||||
if err := contextDone(ctx, "expression evaluation"); err != nil {
|
||||
ev.error(err)
|
||||
}
|
||||
// Reset number of samples in memory after each timestamp.
|
||||
|
@ -1196,12 +1222,12 @@ func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper)
|
|||
for si, series := range matrixes[i] {
|
||||
switch {
|
||||
case len(series.Floats) > 0 && series.Floats[0].T == ts:
|
||||
vectors[i] = append(vectors[i], Sample{Metric: series.Metric, F: series.Floats[0].F, T: ts})
|
||||
vectors[i] = append(vectors[i], Sample{Metric: series.Metric, F: series.Floats[0].F, T: ts, DropName: series.DropName})
|
||||
// Move input vectors forward so we don't have to re-scan the same
|
||||
// past points at the next step.
|
||||
matrixes[i][si].Floats = series.Floats[1:]
|
||||
case len(series.Histograms) > 0 && series.Histograms[0].T == ts:
|
||||
vectors[i] = append(vectors[i], Sample{Metric: series.Metric, H: series.Histograms[0].H, T: ts})
|
||||
vectors[i] = append(vectors[i], Sample{Metric: series.Metric, H: series.Histograms[0].H, T: ts, DropName: series.DropName})
|
||||
matrixes[i][si].Histograms = series.Histograms[1:]
|
||||
default:
|
||||
continue
|
||||
|
@ -1240,15 +1266,15 @@ func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper)
|
|||
|
||||
// If this could be an instant query, shortcut so as not to change sort order.
|
||||
if ev.endTimestamp == ev.startTimestamp {
|
||||
if result.ContainsSameLabelset() {
|
||||
if !ev.enableDelayedNameRemoval && result.ContainsSameLabelset() {
|
||||
ev.errorf("vector cannot contain metrics with the same labelset")
|
||||
}
|
||||
mat := make(Matrix, len(result))
|
||||
for i, s := range result {
|
||||
if s.H == nil {
|
||||
mat[i] = Series{Metric: s.Metric, Floats: []FPoint{{T: ts, F: s.F}}}
|
||||
mat[i] = Series{Metric: s.Metric, Floats: []FPoint{{T: ts, F: s.F}}, DropName: s.DropName}
|
||||
} else {
|
||||
mat[i] = Series{Metric: s.Metric, Histograms: []HPoint{{T: ts, H: s.H}}}
|
||||
mat[i] = Series{Metric: s.Metric, Histograms: []HPoint{{T: ts, H: s.H}}, DropName: s.DropName}
|
||||
}
|
||||
}
|
||||
ev.currentSamples = originalNumSamples + mat.TotalSamples()
|
||||
|
@ -1266,7 +1292,7 @@ func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper)
|
|||
}
|
||||
ss.ts = ts
|
||||
} else {
|
||||
ss = seriesAndTimestamp{Series{Metric: sample.Metric}, ts}
|
||||
ss = seriesAndTimestamp{Series{Metric: sample.Metric, DropName: sample.DropName}, ts}
|
||||
}
|
||||
addToSeries(&ss.Series, enh.Ts, sample.F, sample.H, numSteps)
|
||||
seriess[h] = ss
|
||||
|
@ -1290,7 +1316,7 @@ func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper)
|
|||
return mat, warnings
|
||||
}
|
||||
|
||||
func (ev *evaluator) rangeEvalAgg(aggExpr *parser.AggregateExpr, sortedGrouping []string, inputMatrix Matrix, param float64) (Matrix, annotations.Annotations) {
|
||||
func (ev *evaluator) rangeEvalAgg(ctx context.Context, aggExpr *parser.AggregateExpr, sortedGrouping []string, inputMatrix Matrix, param float64) (Matrix, annotations.Annotations) {
|
||||
// Keep a copy of the original point slice so that it can be returned to the pool.
|
||||
origMatrix := slices.Clone(inputMatrix)
|
||||
defer func() {
|
||||
|
@ -1302,7 +1328,7 @@ func (ev *evaluator) rangeEvalAgg(aggExpr *parser.AggregateExpr, sortedGrouping
|
|||
|
||||
var warnings annotations.Annotations
|
||||
|
||||
enh := &EvalNodeHelper{}
|
||||
enh := &EvalNodeHelper{enableDelayedNameRemoval: ev.enableDelayedNameRemoval}
|
||||
tempNumSamples := ev.currentSamples
|
||||
|
||||
// Create a mapping from input series to output groups.
|
||||
|
@ -1370,7 +1396,7 @@ func (ev *evaluator) rangeEvalAgg(aggExpr *parser.AggregateExpr, sortedGrouping
|
|||
}
|
||||
|
||||
for ts := ev.startTimestamp; ts <= ev.endTimestamp; ts += ev.interval {
|
||||
if err := contextDone(ev.ctx, "expression evaluation"); err != nil {
|
||||
if err := contextDone(ctx, "expression evaluation"); err != nil {
|
||||
ev.error(err)
|
||||
}
|
||||
// Reset number of samples in memory after each timestamp.
|
||||
|
@ -1421,11 +1447,11 @@ func (ev *evaluator) rangeEvalAgg(aggExpr *parser.AggregateExpr, sortedGrouping
|
|||
|
||||
// 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, annotations.Annotations) {
|
||||
func (ev *evaluator) evalSubquery(ctx context.Context, subq *parser.SubqueryExpr) (*parser.MatrixSelector, int, annotations.Annotations) {
|
||||
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)
|
||||
val, ws := ev.eval(ctx, subq)
|
||||
// But do incorporate the peak from the subquery
|
||||
samplesStats.UpdatePeakFromSubquery(ev.samplesStats)
|
||||
ev.samplesStats = samplesStats
|
||||
|
@ -1452,18 +1478,20 @@ func (ev *evaluator) evalSubquery(subq *parser.SubqueryExpr) (*parser.MatrixSele
|
|||
}
|
||||
|
||||
// eval evaluates the given expression as the given AST expression node requires.
|
||||
func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotations) {
|
||||
func (ev *evaluator) eval(ctx context.Context, expr parser.Expr) (parser.Value, annotations.Annotations) {
|
||||
// This is the top-level evaluation method.
|
||||
// Thus, we check for timeout/cancellation here.
|
||||
if err := contextDone(ev.ctx, "expression evaluation"); err != nil {
|
||||
if err := contextDone(ctx, "expression evaluation"); err != nil {
|
||||
ev.error(err)
|
||||
}
|
||||
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
|
||||
ctx, span := otel.Tracer("").Start(ctx, stats.InnerEvalTime.SpanOperation()+" eval "+reflect.TypeOf(expr).String())
|
||||
defer span.End()
|
||||
if ss, ok := expr.(interface{ ShortString() string }); ok {
|
||||
span.SetAttributes(attribute.String("operation", ss.ShortString()))
|
||||
}
|
||||
|
||||
switch e := expr.(type) {
|
||||
case *parser.AggregateExpr:
|
||||
|
@ -1484,7 +1512,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
sortedGrouping = append(sortedGrouping, valueLabel.Val)
|
||||
slices.Sort(sortedGrouping)
|
||||
}
|
||||
return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.rangeEval(ctx, nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.aggregationCountValues(e, sortedGrouping, valueLabel.Val, v[0].(Vector), enh)
|
||||
}, e.Expr)
|
||||
}
|
||||
|
@ -1494,16 +1522,16 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
// param is the number k for topk/bottomk, or q for quantile.
|
||||
var fParam float64
|
||||
if param != nil {
|
||||
val, ws := ev.eval(param)
|
||||
val, ws := ev.eval(ctx, param)
|
||||
warnings.Merge(ws)
|
||||
fParam = val.(Matrix)[0].Floats[0].F
|
||||
}
|
||||
// Now fetch the data to be aggregated.
|
||||
val, ws := ev.eval(e.Expr)
|
||||
val, ws := ev.eval(ctx, e.Expr)
|
||||
warnings.Merge(ws)
|
||||
inputMatrix := val.(Matrix)
|
||||
|
||||
result, ws := ev.rangeEvalAgg(e, sortedGrouping, inputMatrix, fParam)
|
||||
result, ws := ev.rangeEvalAgg(ctx, e, sortedGrouping, inputMatrix, fParam)
|
||||
warnings.Merge(ws)
|
||||
ev.currentSamples = originalNumSamples + result.TotalSamples()
|
||||
ev.samplesStats.UpdatePeak(ev.currentSamples)
|
||||
|
@ -1521,7 +1549,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
unwrapParenExpr(&arg)
|
||||
vs, ok := arg.(*parser.VectorSelector)
|
||||
if ok {
|
||||
return ev.rangeEvalTimestampFunctionOverVectorSelector(vs, call, e)
|
||||
return ev.rangeEvalTimestampFunctionOverVectorSelector(ctx, vs, call, e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1545,7 +1573,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
matrixArgIndex = i
|
||||
matrixArg = true
|
||||
// Replacing parser.SubqueryExpr with parser.MatrixSelector.
|
||||
val, totalSamples, ws := ev.evalSubquery(subq)
|
||||
val, totalSamples, ws := ev.evalSubquery(ctx, subq)
|
||||
e.Args[i] = val
|
||||
warnings.Merge(ws)
|
||||
defer func() {
|
||||
|
@ -1560,14 +1588,14 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
// Special handling for functions that work on series not samples.
|
||||
switch e.Func.Name {
|
||||
case "label_replace":
|
||||
return ev.evalLabelReplace(e.Args)
|
||||
return ev.evalLabelReplace(ctx, e.Args)
|
||||
case "label_join":
|
||||
return ev.evalLabelJoin(e.Args)
|
||||
return ev.evalLabelJoin(ctx, e.Args)
|
||||
}
|
||||
|
||||
if !matrixArg {
|
||||
// Does not have a matrix argument.
|
||||
return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.rangeEval(ctx, nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
vec, annos := call(v, e.Args, enh)
|
||||
return vec, warnings.Merge(annos)
|
||||
}, e.Args...)
|
||||
|
@ -1579,7 +1607,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
otherInArgs := make([]Vector, len(e.Args))
|
||||
for i, e := range e.Args {
|
||||
if i != matrixArgIndex {
|
||||
val, ws := ev.eval(e)
|
||||
val, ws := ev.eval(ctx, e)
|
||||
otherArgs[i] = val.(Matrix)
|
||||
otherInArgs[i] = Vector{Sample{}}
|
||||
inArgs[i] = otherInArgs[i]
|
||||
|
@ -1593,7 +1621,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
sel := arg.(*parser.MatrixSelector)
|
||||
selVS := sel.VectorSelector.(*parser.VectorSelector)
|
||||
|
||||
ws, err := checkAndExpandSeriesSet(ev.ctx, sel)
|
||||
ws, err := checkAndExpandSeriesSet(ctx, sel)
|
||||
warnings.Merge(ws)
|
||||
if err != nil {
|
||||
ev.error(errWithWarnings{fmt.Errorf("expanding series: %w", err), warnings})
|
||||
|
@ -1611,12 +1639,19 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
var prevSS *Series
|
||||
inMatrix := make(Matrix, 1)
|
||||
inArgs[matrixArgIndex] = inMatrix
|
||||
enh := &EvalNodeHelper{Out: make(Vector, 0, 1)}
|
||||
enh := &EvalNodeHelper{Out: make(Vector, 0, 1), enableDelayedNameRemoval: ev.enableDelayedNameRemoval}
|
||||
// Process all the calls for one time series at a time.
|
||||
it := storage.NewBuffer(selRange)
|
||||
var chkIter chunkenc.Iterator
|
||||
|
||||
// The last_over_time function acts like offset; thus, it
|
||||
// should keep the metric name. For all the other range
|
||||
// vector functions, the only change needed is to drop the
|
||||
// metric name in the output.
|
||||
dropName := e.Func.Name != "last_over_time"
|
||||
|
||||
for i, s := range selVS.Series {
|
||||
if err := contextDone(ev.ctx, "expression evaluation"); err != nil {
|
||||
if err := contextDone(ctx, "expression evaluation"); err != nil {
|
||||
ev.error(err)
|
||||
}
|
||||
ev.currentSamples -= len(floats) + totalHPointSize(histograms)
|
||||
|
@ -1629,15 +1664,12 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
chkIter = s.Iterator(chkIter)
|
||||
it.Reset(chkIter)
|
||||
metric := selVS.Series[i].Labels()
|
||||
// The last_over_time function acts like offset; thus, it
|
||||
// should keep the metric name. For all the other range
|
||||
// vector functions, the only change needed is to drop the
|
||||
// metric name in the output.
|
||||
if e.Func.Name != "last_over_time" {
|
||||
if !ev.enableDelayedNameRemoval && dropName {
|
||||
metric = metric.DropMetricName()
|
||||
}
|
||||
ss := Series{
|
||||
Metric: metric,
|
||||
Metric: metric,
|
||||
DropName: dropName,
|
||||
}
|
||||
inMatrix[0].Metric = selVS.Series[i].Labels()
|
||||
for ts, step := ev.startTimestamp, -1; ts <= ev.endTimestamp; ts += ev.interval {
|
||||
|
@ -1752,32 +1784,35 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
|
||||
return Matrix{
|
||||
Series{
|
||||
Metric: createLabelsForAbsentFunction(e.Args[0]),
|
||||
Floats: newp,
|
||||
Metric: createLabelsForAbsentFunction(e.Args[0]),
|
||||
Floats: newp,
|
||||
DropName: dropName,
|
||||
},
|
||||
}, warnings
|
||||
}
|
||||
|
||||
if mat.ContainsSameLabelset() {
|
||||
if !ev.enableDelayedNameRemoval && mat.ContainsSameLabelset() {
|
||||
ev.errorf("vector cannot contain metrics with the same labelset")
|
||||
}
|
||||
|
||||
return mat, warnings
|
||||
|
||||
case *parser.ParenExpr:
|
||||
return ev.eval(e.Expr)
|
||||
return ev.eval(ctx, e.Expr)
|
||||
|
||||
case *parser.UnaryExpr:
|
||||
val, ws := ev.eval(e.Expr)
|
||||
val, ws := ev.eval(ctx, e.Expr)
|
||||
mat := val.(Matrix)
|
||||
if e.Op == parser.SUB {
|
||||
for i := range mat {
|
||||
mat[i].Metric = mat[i].Metric.DropMetricName()
|
||||
if !ev.enableDelayedNameRemoval {
|
||||
mat[i].Metric = mat[i].Metric.DropMetricName()
|
||||
}
|
||||
mat[i].DropName = true
|
||||
for j := range mat[i].Floats {
|
||||
mat[i].Floats[j].F = -mat[i].Floats[j].F
|
||||
}
|
||||
}
|
||||
if mat.ContainsSameLabelset() {
|
||||
if !ev.enableDelayedNameRemoval && mat.ContainsSameLabelset() {
|
||||
ev.errorf("vector cannot contain metrics with the same labelset")
|
||||
}
|
||||
}
|
||||
|
@ -1786,7 +1821,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
case *parser.BinaryExpr:
|
||||
switch lt, rt := e.LHS.Type(), e.RHS.Type(); {
|
||||
case lt == parser.ValueTypeScalar && rt == parser.ValueTypeScalar:
|
||||
return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.rangeEval(ctx, nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
val := scalarBinop(e.Op, v[0].(Vector)[0].F, v[1].(Vector)[0].F)
|
||||
return append(enh.Out, Sample{F: val}), nil
|
||||
}, e.LHS, e.RHS)
|
||||
|
@ -1799,47 +1834,49 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
}
|
||||
switch e.Op {
|
||||
case parser.LAND:
|
||||
return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.rangeEval(ctx, initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.VectorAnd(v[0].(Vector), v[1].(Vector), e.VectorMatching, sh[0], sh[1], enh), nil
|
||||
}, e.LHS, e.RHS)
|
||||
case parser.LOR:
|
||||
return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.rangeEval(ctx, initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.VectorOr(v[0].(Vector), v[1].(Vector), e.VectorMatching, sh[0], sh[1], enh), nil
|
||||
}, e.LHS, e.RHS)
|
||||
case parser.LUNLESS:
|
||||
return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.rangeEval(ctx, initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.VectorUnless(v[0].(Vector), v[1].(Vector), e.VectorMatching, sh[0], sh[1], enh), nil
|
||||
}, e.LHS, e.RHS)
|
||||
default:
|
||||
return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.rangeEval(ctx, initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
vec, err := ev.VectorBinop(e.Op, v[0].(Vector), v[1].(Vector), e.VectorMatching, e.ReturnBool, sh[0], sh[1], enh)
|
||||
return vec, handleVectorBinopError(err, e)
|
||||
}, e.LHS, e.RHS)
|
||||
}
|
||||
|
||||
case lt == parser.ValueTypeVector && rt == parser.ValueTypeScalar:
|
||||
return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.rangeEval(ctx, nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
vec, err := ev.VectorscalarBinop(e.Op, v[0].(Vector), Scalar{V: v[1].(Vector)[0].F}, false, e.ReturnBool, enh)
|
||||
return vec, handleVectorBinopError(err, e)
|
||||
}, e.LHS, e.RHS)
|
||||
|
||||
case lt == parser.ValueTypeScalar && rt == parser.ValueTypeVector:
|
||||
return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.rangeEval(ctx, nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
vec, err := ev.VectorscalarBinop(e.Op, v[1].(Vector), Scalar{V: v[0].(Vector)[0].F}, true, e.ReturnBool, enh)
|
||||
return vec, handleVectorBinopError(err, e)
|
||||
}, e.LHS, e.RHS)
|
||||
}
|
||||
|
||||
case *parser.NumberLiteral:
|
||||
return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
span.SetAttributes(attribute.Float64("value", e.Val))
|
||||
return ev.rangeEval(ctx, nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return append(enh.Out, Sample{F: e.Val, Metric: labels.EmptyLabels()}), nil
|
||||
})
|
||||
|
||||
case *parser.StringLiteral:
|
||||
span.SetAttributes(attribute.String("value", e.Val))
|
||||
return String{V: e.Val, T: ev.startTimestamp}, nil
|
||||
|
||||
case *parser.VectorSelector:
|
||||
ws, err := checkAndExpandSeriesSet(ev.ctx, e)
|
||||
ws, err := checkAndExpandSeriesSet(ctx, e)
|
||||
if err != nil {
|
||||
ev.error(errWithWarnings{fmt.Errorf("expanding series: %w", err), ws})
|
||||
}
|
||||
|
@ -1848,7 +1885,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
it := storage.NewMemoizedEmptyIterator(durationMilliseconds(ev.lookbackDelta))
|
||||
var chkIter chunkenc.Iterator
|
||||
for i, s := range e.Series {
|
||||
if err := contextDone(ev.ctx, "expression evaluation"); err != nil {
|
||||
if err := contextDone(ctx, "expression evaluation"); err != nil {
|
||||
ev.error(err)
|
||||
}
|
||||
chkIter = s.Iterator(chkIter)
|
||||
|
@ -1899,20 +1936,20 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
if ev.startTimestamp != ev.endTimestamp {
|
||||
panic(errors.New("cannot do range evaluation of matrix selector"))
|
||||
}
|
||||
return ev.matrixSelector(e)
|
||||
return ev.matrixSelector(ctx, e)
|
||||
|
||||
case *parser.SubqueryExpr:
|
||||
offsetMillis := durationMilliseconds(e.Offset)
|
||||
rangeMillis := durationMilliseconds(e.Range)
|
||||
newEv := &evaluator{
|
||||
endTimestamp: ev.endTimestamp - offsetMillis,
|
||||
ctx: ev.ctx,
|
||||
currentSamples: ev.currentSamples,
|
||||
maxSamples: ev.maxSamples,
|
||||
logger: ev.logger,
|
||||
lookbackDelta: ev.lookbackDelta,
|
||||
samplesStats: ev.samplesStats.NewChild(),
|
||||
noStepSubqueryIntervalFn: ev.noStepSubqueryIntervalFn,
|
||||
enableDelayedNameRemoval: ev.enableDelayedNameRemoval,
|
||||
}
|
||||
|
||||
if e.Step != 0 {
|
||||
|
@ -1935,7 +1972,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
setOffsetForAtModifier(newEv.startTimestamp, e.Expr)
|
||||
}
|
||||
|
||||
res, ws := newEv.eval(e.Expr)
|
||||
res, ws := newEv.eval(ctx, e.Expr)
|
||||
ev.currentSamples = newEv.currentSamples
|
||||
ev.samplesStats.UpdatePeakFromSubquery(newEv.samplesStats)
|
||||
ev.samplesStats.IncrementSamplesAtTimestamp(ev.endTimestamp, newEv.samplesStats.TotalSamples)
|
||||
|
@ -1943,22 +1980,22 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
|||
case *parser.StepInvariantExpr:
|
||||
switch ce := e.Expr.(type) {
|
||||
case *parser.StringLiteral, *parser.NumberLiteral:
|
||||
return ev.eval(ce)
|
||||
return ev.eval(ctx, ce)
|
||||
}
|
||||
|
||||
newEv := &evaluator{
|
||||
startTimestamp: ev.startTimestamp,
|
||||
endTimestamp: ev.startTimestamp, // Always a single evaluation.
|
||||
interval: ev.interval,
|
||||
ctx: ev.ctx,
|
||||
currentSamples: ev.currentSamples,
|
||||
maxSamples: ev.maxSamples,
|
||||
logger: ev.logger,
|
||||
lookbackDelta: ev.lookbackDelta,
|
||||
samplesStats: ev.samplesStats.NewChild(),
|
||||
noStepSubqueryIntervalFn: ev.noStepSubqueryIntervalFn,
|
||||
enableDelayedNameRemoval: ev.enableDelayedNameRemoval,
|
||||
}
|
||||
res, ws := newEv.eval(e.Expr)
|
||||
res, ws := newEv.eval(ctx, e.Expr)
|
||||
ev.currentSamples = newEv.currentSamples
|
||||
ev.samplesStats.UpdatePeakFromSubquery(newEv.samplesStats)
|
||||
for ts, step := ev.startTimestamp, -1; ts <= ev.endTimestamp; ts += ev.interval {
|
||||
|
@ -2034,8 +2071,8 @@ func reuseOrGetFPointSlices(prevSS *Series, numSteps int) (r []FPoint) {
|
|||
return getFPointSlice(numSteps)
|
||||
}
|
||||
|
||||
func (ev *evaluator) rangeEvalTimestampFunctionOverVectorSelector(vs *parser.VectorSelector, call FunctionCall, e *parser.Call) (parser.Value, annotations.Annotations) {
|
||||
ws, err := checkAndExpandSeriesSet(ev.ctx, vs)
|
||||
func (ev *evaluator) rangeEvalTimestampFunctionOverVectorSelector(ctx context.Context, vs *parser.VectorSelector, call FunctionCall, e *parser.Call) (parser.Value, annotations.Annotations) {
|
||||
ws, err := checkAndExpandSeriesSet(ctx, vs)
|
||||
if err != nil {
|
||||
ev.error(errWithWarnings{fmt.Errorf("expanding series: %w", err), ws})
|
||||
}
|
||||
|
@ -2046,7 +2083,7 @@ func (ev *evaluator) rangeEvalTimestampFunctionOverVectorSelector(vs *parser.Vec
|
|||
seriesIterators[i] = storage.NewMemoizedIterator(it, durationMilliseconds(ev.lookbackDelta))
|
||||
}
|
||||
|
||||
return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return ev.rangeEval(ctx, nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
if vs.Timestamp != nil {
|
||||
// This is a special case for "timestamp()" when the @ modifier is used, to ensure that
|
||||
// we return a point for each time step in this case.
|
||||
|
@ -2182,7 +2219,7 @@ func putMatrixSelectorHPointSlice(p []HPoint) {
|
|||
}
|
||||
|
||||
// matrixSelector evaluates a *parser.MatrixSelector expression.
|
||||
func (ev *evaluator) matrixSelector(node *parser.MatrixSelector) (Matrix, annotations.Annotations) {
|
||||
func (ev *evaluator) matrixSelector(ctx context.Context, node *parser.MatrixSelector) (Matrix, annotations.Annotations) {
|
||||
var (
|
||||
vs = node.VectorSelector.(*parser.VectorSelector)
|
||||
|
||||
|
@ -2193,7 +2230,7 @@ func (ev *evaluator) matrixSelector(node *parser.MatrixSelector) (Matrix, annota
|
|||
|
||||
it = storage.NewBuffer(durationMilliseconds(node.Range))
|
||||
)
|
||||
ws, err := checkAndExpandSeriesSet(ev.ctx, node)
|
||||
ws, err := checkAndExpandSeriesSet(ctx, node)
|
||||
if err != nil {
|
||||
ev.error(errWithWarnings{fmt.Errorf("expanding series: %w", err), ws})
|
||||
}
|
||||
|
@ -2201,7 +2238,7 @@ func (ev *evaluator) matrixSelector(node *parser.MatrixSelector) (Matrix, annota
|
|||
var chkIter chunkenc.Iterator
|
||||
series := vs.Series
|
||||
for i, s := range series {
|
||||
if err := contextDone(ev.ctx, "expression evaluation"); err != nil {
|
||||
if err := contextDone(ctx, "expression evaluation"); err != nil {
|
||||
ev.error(err)
|
||||
}
|
||||
chkIter = s.Iterator(chkIter)
|
||||
|
@ -2553,7 +2590,7 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
|
|||
continue
|
||||
}
|
||||
metric := resultMetric(ls.Metric, rs.Metric, op, matching, enh)
|
||||
if returnBool {
|
||||
if !ev.enableDelayedNameRemoval && returnBool {
|
||||
metric = metric.DropMetricName()
|
||||
}
|
||||
insertedSigs, exists := matchedSigs[sig]
|
||||
|
@ -2578,9 +2615,10 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
|
|||
}
|
||||
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: metric,
|
||||
F: floatValue,
|
||||
H: histogramValue,
|
||||
Metric: metric,
|
||||
F: floatValue,
|
||||
H: histogramValue,
|
||||
DropName: returnBool,
|
||||
})
|
||||
}
|
||||
return enh.Out, lastErr
|
||||
|
@ -2680,7 +2718,10 @@ func (ev *evaluator) VectorscalarBinop(op parser.ItemType, lhs Vector, rhs Scala
|
|||
lhsSample.F = float
|
||||
lhsSample.H = histogram
|
||||
if shouldDropMetricName(op) || returnBool {
|
||||
lhsSample.Metric = lhsSample.Metric.DropMetricName()
|
||||
if !ev.enableDelayedNameRemoval {
|
||||
lhsSample.Metric = lhsSample.Metric.DropMetricName()
|
||||
}
|
||||
lhsSample.DropName = true
|
||||
}
|
||||
enh.Out = append(enh.Out, lhsSample)
|
||||
}
|
||||
|
@ -3019,6 +3060,7 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix
|
|||
|
||||
ss := &outputMatrix[ri]
|
||||
addToSeries(ss, enh.Ts, aggr.floatValue, aggr.histogramValue, numSteps)
|
||||
ss.DropName = inputMatrix[ri].DropName
|
||||
}
|
||||
|
||||
return annos
|
||||
|
@ -3045,7 +3087,7 @@ seriesLoop:
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
s = Sample{Metric: inputMatrix[si].Metric, F: f}
|
||||
s = Sample{Metric: inputMatrix[si].Metric, F: f, DropName: inputMatrix[si].DropName}
|
||||
|
||||
group := &groups[seriesToResult[si]]
|
||||
// Initialize this group if it's the first time we've seen it.
|
||||
|
@ -3129,16 +3171,16 @@ seriesLoop:
|
|||
mat = make(Matrix, 0, len(groups))
|
||||
}
|
||||
|
||||
add := func(lbls labels.Labels, f float64) {
|
||||
add := func(lbls labels.Labels, f float64, dropName bool) {
|
||||
// If this could be an instant query, add directly to the matrix so the result is in consistent order.
|
||||
if ev.endTimestamp == ev.startTimestamp {
|
||||
mat = append(mat, Series{Metric: lbls, Floats: []FPoint{{T: enh.Ts, F: f}}})
|
||||
mat = append(mat, Series{Metric: lbls, Floats: []FPoint{{T: enh.Ts, F: f}}, DropName: dropName})
|
||||
} else {
|
||||
// Otherwise the results are added into seriess elements.
|
||||
hash := lbls.Hash()
|
||||
ss, ok := seriess[hash]
|
||||
if !ok {
|
||||
ss = Series{Metric: lbls}
|
||||
ss = Series{Metric: lbls, DropName: dropName}
|
||||
}
|
||||
addToSeries(&ss, enh.Ts, f, nil, numSteps)
|
||||
seriess[hash] = ss
|
||||
|
@ -3155,7 +3197,7 @@ seriesLoop:
|
|||
sort.Sort(sort.Reverse(aggr.heap))
|
||||
}
|
||||
for _, v := range aggr.heap {
|
||||
add(v.Metric, v.F)
|
||||
add(v.Metric, v.F, v.DropName)
|
||||
}
|
||||
|
||||
case parser.BOTTOMK:
|
||||
|
@ -3164,12 +3206,12 @@ seriesLoop:
|
|||
sort.Sort(sort.Reverse((*vectorByReverseValueHeap)(&aggr.heap)))
|
||||
}
|
||||
for _, v := range aggr.heap {
|
||||
add(v.Metric, v.F)
|
||||
add(v.Metric, v.F, v.DropName)
|
||||
}
|
||||
|
||||
case parser.LIMITK, parser.LIMIT_RATIO:
|
||||
for _, v := range aggr.heap {
|
||||
add(v.Metric, v.F)
|
||||
add(v.Metric, v.F, v.DropName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3221,6 +3263,30 @@ func (ev *evaluator) aggregationCountValues(e *parser.AggregateExpr, grouping []
|
|||
return enh.Out, nil
|
||||
}
|
||||
|
||||
func (ev *evaluator) cleanupMetricLabels(v parser.Value) {
|
||||
if v.Type() == parser.ValueTypeMatrix {
|
||||
mat := v.(Matrix)
|
||||
for i := range mat {
|
||||
if mat[i].DropName {
|
||||
mat[i].Metric = mat[i].Metric.DropMetricName()
|
||||
}
|
||||
}
|
||||
if mat.ContainsSameLabelset() {
|
||||
ev.errorf("vector cannot contain metrics with the same labelset")
|
||||
}
|
||||
} else if v.Type() == parser.ValueTypeVector {
|
||||
vec := v.(Vector)
|
||||
for i := range vec {
|
||||
if vec[i].DropName {
|
||||
vec[i].Metric = vec[i].Metric.DropMetricName()
|
||||
}
|
||||
}
|
||||
if vec.ContainsSameLabelset() {
|
||||
ev.errorf("vector cannot contain metrics with the same labelset")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addToSeries(ss *Series, ts int64, f float64, h *histogram.FloatHistogram, numSteps int) {
|
||||
if h == nil {
|
||||
if ss.Floats == nil {
|
||||
|
|
|
@ -17,8 +17,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
@ -56,14 +54,7 @@ func TestMain(m *testing.M) {
|
|||
func TestQueryConcurrency(t *testing.T) {
|
||||
maxConcurrency := 10
|
||||
|
||||
dir, err := os.MkdirTemp("", "test_concurrency")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
queryTracker := promql.NewActiveQueryTracker(dir, maxConcurrency, nil)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, queryTracker.Close())
|
||||
})
|
||||
|
||||
queryTracker := promql.NewActiveQueryTracker(t.TempDir(), maxConcurrency, nil)
|
||||
opts := promql.EngineOpts{
|
||||
Logger: nil,
|
||||
Reg: nil,
|
||||
|
@ -71,15 +62,17 @@ func TestQueryConcurrency(t *testing.T) {
|
|||
Timeout: 100 * time.Second,
|
||||
ActiveQueryTracker: queryTracker,
|
||||
}
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
|
||||
engine := promql.NewEngine(opts)
|
||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||
defer cancelCtx()
|
||||
t.Cleanup(cancelCtx)
|
||||
|
||||
block := make(chan struct{})
|
||||
processing := make(chan struct{})
|
||||
done := make(chan int)
|
||||
defer close(done)
|
||||
t.Cleanup(func() {
|
||||
close(done)
|
||||
})
|
||||
|
||||
f := func(context.Context) error {
|
||||
select {
|
||||
|
@ -164,7 +157,7 @@ func TestQueryTimeout(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 5 * time.Millisecond,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||
defer cancelCtx()
|
||||
|
||||
|
@ -189,7 +182,7 @@ func TestQueryCancel(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||
defer cancelCtx()
|
||||
|
||||
|
@ -263,7 +256,7 @@ func TestQueryError(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
errStorage := promql.ErrStorage{errors.New("storage error")}
|
||||
queryable := storage.QueryableFunc(func(mint, maxt int64) (storage.Querier, error) {
|
||||
return &errQuerier{err: errStorage}, nil
|
||||
|
@ -597,7 +590,7 @@ func TestSelectHintsSetCorrectly(t *testing.T) {
|
|||
},
|
||||
} {
|
||||
t.Run(tc.query, func(t *testing.T) {
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
hintsRecorder := &noopHintRecordingQueryable{}
|
||||
|
||||
var (
|
||||
|
@ -628,7 +621,7 @@ func TestEngineShutdown(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||
|
||||
block := make(chan struct{})
|
||||
|
@ -764,7 +757,7 @@ load 10s
|
|||
t.Run(fmt.Sprintf("%d query=%s", i, c.Query), func(t *testing.T) {
|
||||
var err error
|
||||
var qry promql.Query
|
||||
engine := newTestEngine()
|
||||
engine := newTestEngine(t)
|
||||
if c.Interval == 0 {
|
||||
qry, err = engine.NewInstantQuery(context.Background(), storage, nil, c.Query, c.Start)
|
||||
} else {
|
||||
|
@ -1306,7 +1299,7 @@ load 10s
|
|||
for _, c := range cases {
|
||||
t.Run(c.Query, func(t *testing.T) {
|
||||
opts := promql.NewPrometheusQueryOpts(true, 0)
|
||||
engine := promqltest.NewTestEngine(true, 0, promqltest.DefaultMaxSamplesPerQuery)
|
||||
engine := promqltest.NewTestEngine(t, true, 0, promqltest.DefaultMaxSamplesPerQuery)
|
||||
|
||||
runQuery := func(expErr error) *stats.Statistics {
|
||||
var err error
|
||||
|
@ -1333,7 +1326,7 @@ load 10s
|
|||
if c.SkipMaxCheck {
|
||||
return
|
||||
}
|
||||
engine = promqltest.NewTestEngine(true, 0, stats.Samples.PeakSamples-1)
|
||||
engine = promqltest.NewTestEngine(t, true, 0, stats.Samples.PeakSamples-1)
|
||||
runQuery(promql.ErrTooManySamples(env))
|
||||
})
|
||||
}
|
||||
|
@ -1486,7 +1479,7 @@ load 10s
|
|||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Query, func(t *testing.T) {
|
||||
engine := newTestEngine()
|
||||
engine := newTestEngine(t)
|
||||
testFunc := func(expError error) {
|
||||
var err error
|
||||
var qry promql.Query
|
||||
|
@ -1507,18 +1500,18 @@ load 10s
|
|||
}
|
||||
|
||||
// Within limit.
|
||||
engine = promqltest.NewTestEngine(false, 0, c.MaxSamples)
|
||||
engine = promqltest.NewTestEngine(t, false, 0, c.MaxSamples)
|
||||
testFunc(nil)
|
||||
|
||||
// Exceeding limit.
|
||||
engine = promqltest.NewTestEngine(false, 0, c.MaxSamples-1)
|
||||
engine = promqltest.NewTestEngine(t, false, 0, c.MaxSamples-1)
|
||||
testFunc(promql.ErrTooManySamples(env))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAtModifier(t *testing.T) {
|
||||
engine := newTestEngine()
|
||||
engine := newTestEngine(t)
|
||||
storage := promqltest.LoadedStorage(t, `
|
||||
load 10s
|
||||
metric{job="1"} 0+1x1000
|
||||
|
@ -1714,7 +1707,8 @@ load 1ms
|
|||
{F: 3600, T: 6 * 60 * 1000},
|
||||
{F: 3600, T: 7 * 60 * 1000},
|
||||
},
|
||||
Metric: labels.EmptyLabels(),
|
||||
Metric: labels.EmptyLabels(),
|
||||
DropName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1930,20 +1924,24 @@ func TestSubquerySelector(t *testing.T) {
|
|||
nil,
|
||||
promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 3, T: 7985000}, {F: 3, T: 7990000}, {F: 3, T: 7995000}, {F: 3, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "0", "group", "canary"),
|
||||
Floats: []promql.FPoint{{F: 3, T: 7985000}, {F: 3, T: 7990000}, {F: 3, T: 7995000}, {F: 3, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "0", "group", "canary"),
|
||||
DropName: true,
|
||||
},
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 4, T: 7985000}, {F: 4, T: 7990000}, {F: 4, T: 7995000}, {F: 4, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "1", "group", "canary"),
|
||||
Floats: []promql.FPoint{{F: 4, T: 7985000}, {F: 4, T: 7990000}, {F: 4, T: 7995000}, {F: 4, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "1", "group", "canary"),
|
||||
DropName: true,
|
||||
},
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 1, T: 7985000}, {F: 1, T: 7990000}, {F: 1, T: 7995000}, {F: 1, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "0", "group", "production"),
|
||||
Floats: []promql.FPoint{{F: 1, T: 7985000}, {F: 1, T: 7990000}, {F: 1, T: 7995000}, {F: 1, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "0", "group", "production"),
|
||||
DropName: true,
|
||||
},
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 2, T: 7985000}, {F: 2, T: 7990000}, {F: 2, T: 7995000}, {F: 2, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "1", "group", "production"),
|
||||
Floats: []promql.FPoint{{F: 2, T: 7985000}, {F: 2, T: 7990000}, {F: 2, T: 7995000}, {F: 2, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "1", "group", "production"),
|
||||
DropName: true,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
|
@ -1996,7 +1994,7 @@ func TestSubquerySelector(t *testing.T) {
|
|||
},
|
||||
} {
|
||||
t.Run("", func(t *testing.T) {
|
||||
engine := newTestEngine()
|
||||
engine := newTestEngine(t)
|
||||
storage := promqltest.LoadedStorage(t, tst.loadString)
|
||||
t.Cleanup(func() { storage.Close() })
|
||||
|
||||
|
@ -2045,7 +2043,7 @@ func TestQueryLogger_basic(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
|
||||
queryExec := func() {
|
||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||
|
@ -2096,7 +2094,7 @@ func TestQueryLogger_fields(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
|
||||
f1 := NewFakeQueryLogger()
|
||||
engine.SetQueryLogger(f1)
|
||||
|
@ -2125,7 +2123,7 @@ func TestQueryLogger_error(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
|
||||
f1 := NewFakeQueryLogger()
|
||||
engine.SetQueryLogger(f1)
|
||||
|
@ -3008,7 +3006,7 @@ func TestEngineOptsValidation(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, c := range cases {
|
||||
eng := promql.NewEngine(c.opts)
|
||||
eng := promqltest.NewTestEngineWithOpts(t, c.opts)
|
||||
_, err1 := eng.NewInstantQuery(context.Background(), nil, nil, c.query, time.Unix(10, 0))
|
||||
_, err2 := eng.NewRangeQuery(context.Background(), nil, nil, c.query, time.Unix(0, 0), time.Unix(10, 0), time.Second)
|
||||
if c.fail {
|
||||
|
@ -3022,7 +3020,7 @@ func TestEngineOptsValidation(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInstantQueryWithRangeVectorSelector(t *testing.T) {
|
||||
engine := newTestEngine()
|
||||
engine := newTestEngine(t)
|
||||
|
||||
baseT := timestamp.Time(0)
|
||||
storage := promqltest.LoadedStorage(t, `
|
||||
|
@ -3276,7 +3274,7 @@ func TestNativeHistogram_SubOperator(t *testing.T) {
|
|||
for _, c := range cases {
|
||||
for _, floatHisto := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("floatHistogram=%t %d", floatHisto, idx0), func(t *testing.T) {
|
||||
engine := newTestEngine()
|
||||
engine := newTestEngine(t)
|
||||
storage := teststorage.New(t)
|
||||
t.Cleanup(func() { storage.Close() })
|
||||
|
||||
|
@ -3332,171 +3330,6 @@ func TestNativeHistogram_SubOperator(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNativeHistogram_MulDivOperator(t *testing.T) {
|
||||
// TODO(codesome): Integrate histograms into the PromQL testing framework
|
||||
// and write more tests there.
|
||||
originalHistogram := histogram.Histogram{
|
||||
Schema: 0,
|
||||
Count: 21,
|
||||
Sum: 33,
|
||||
ZeroThreshold: 0.001,
|
||||
ZeroCount: 3,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
},
|
||||
PositiveBuckets: []int64{3, 0, 0},
|
||||
NegativeSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
},
|
||||
NegativeBuckets: []int64{3, 0, 0},
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
scalar float64
|
||||
histogram histogram.Histogram
|
||||
expectedMul histogram.FloatHistogram
|
||||
expectedDiv histogram.FloatHistogram
|
||||
}{
|
||||
{
|
||||
scalar: 3,
|
||||
histogram: originalHistogram,
|
||||
expectedMul: histogram.FloatHistogram{
|
||||
Schema: 0,
|
||||
Count: 63,
|
||||
Sum: 99,
|
||||
ZeroThreshold: 0.001,
|
||||
ZeroCount: 9,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
},
|
||||
PositiveBuckets: []float64{9, 9, 9},
|
||||
NegativeSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
},
|
||||
NegativeBuckets: []float64{9, 9, 9},
|
||||
},
|
||||
expectedDiv: histogram.FloatHistogram{
|
||||
Schema: 0,
|
||||
Count: 7,
|
||||
Sum: 11,
|
||||
ZeroThreshold: 0.001,
|
||||
ZeroCount: 1,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
},
|
||||
PositiveBuckets: []float64{1, 1, 1},
|
||||
NegativeSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
},
|
||||
NegativeBuckets: []float64{1, 1, 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
scalar: 0,
|
||||
histogram: originalHistogram,
|
||||
expectedMul: histogram.FloatHistogram{
|
||||
Schema: 0,
|
||||
Count: 0,
|
||||
Sum: 0,
|
||||
ZeroThreshold: 0.001,
|
||||
ZeroCount: 0,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
},
|
||||
PositiveBuckets: []float64{0, 0, 0},
|
||||
NegativeSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
},
|
||||
NegativeBuckets: []float64{0, 0, 0},
|
||||
},
|
||||
expectedDiv: histogram.FloatHistogram{
|
||||
Schema: 0,
|
||||
Count: math.Inf(1),
|
||||
Sum: math.Inf(1),
|
||||
ZeroThreshold: 0.001,
|
||||
ZeroCount: math.Inf(1),
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
},
|
||||
PositiveBuckets: []float64{math.Inf(1), math.Inf(1), math.Inf(1)},
|
||||
NegativeSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
},
|
||||
NegativeBuckets: []float64{math.Inf(1), math.Inf(1), math.Inf(1)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
idx0 := int64(0)
|
||||
for _, c := range cases {
|
||||
for _, floatHisto := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("floatHistogram=%t %d", floatHisto, idx0), func(t *testing.T) {
|
||||
storage := teststorage.New(t)
|
||||
t.Cleanup(func() { storage.Close() })
|
||||
|
||||
seriesName := "sparse_histogram_series"
|
||||
floatSeriesName := "float_series"
|
||||
|
||||
engine := newTestEngine()
|
||||
|
||||
ts := idx0 * int64(10*time.Minute/time.Millisecond)
|
||||
app := storage.Appender(context.Background())
|
||||
h := c.histogram
|
||||
lbls := labels.FromStrings("__name__", seriesName)
|
||||
// Since we mutate h later, we need to create a copy here.
|
||||
var err error
|
||||
if floatHisto {
|
||||
_, err = app.AppendHistogram(0, lbls, ts, nil, h.Copy().ToFloat(nil))
|
||||
} else {
|
||||
_, err = app.AppendHistogram(0, lbls, ts, h.Copy(), nil)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
_, err = app.Append(0, labels.FromStrings("__name__", floatSeriesName), ts, c.scalar)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, app.Commit())
|
||||
|
||||
queryAndCheck := func(queryString string, exp promql.Vector) {
|
||||
qry, err := engine.NewInstantQuery(context.Background(), storage, nil, queryString, timestamp.Time(ts))
|
||||
require.NoError(t, err)
|
||||
|
||||
res := qry.Exec(context.Background())
|
||||
require.NoError(t, res.Err)
|
||||
|
||||
vector, err := res.Vector()
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.RequireEqual(t, exp, vector)
|
||||
}
|
||||
|
||||
// histogram * scalar.
|
||||
queryString := fmt.Sprintf(`%s * %f`, seriesName, c.scalar)
|
||||
queryAndCheck(queryString, []promql.Sample{{T: ts, H: &c.expectedMul, Metric: labels.EmptyLabels()}})
|
||||
|
||||
// scalar * histogram.
|
||||
queryString = fmt.Sprintf(`%f * %s`, c.scalar, seriesName)
|
||||
queryAndCheck(queryString, []promql.Sample{{T: ts, H: &c.expectedMul, Metric: labels.EmptyLabels()}})
|
||||
|
||||
// histogram * float.
|
||||
queryString = fmt.Sprintf(`%s * %s`, seriesName, floatSeriesName)
|
||||
queryAndCheck(queryString, []promql.Sample{{T: ts, H: &c.expectedMul, Metric: labels.EmptyLabels()}})
|
||||
|
||||
// float * histogram.
|
||||
queryString = fmt.Sprintf(`%s * %s`, floatSeriesName, seriesName)
|
||||
queryAndCheck(queryString, []promql.Sample{{T: ts, H: &c.expectedMul, Metric: labels.EmptyLabels()}})
|
||||
|
||||
// histogram / scalar.
|
||||
queryString = fmt.Sprintf(`%s / %f`, seriesName, c.scalar)
|
||||
queryAndCheck(queryString, []promql.Sample{{T: ts, H: &c.expectedDiv, Metric: labels.EmptyLabels()}})
|
||||
|
||||
// histogram / float.
|
||||
queryString = fmt.Sprintf(`%s / %s`, seriesName, floatSeriesName)
|
||||
queryAndCheck(queryString, []promql.Sample{{T: ts, H: &c.expectedDiv, Metric: labels.EmptyLabels()}})
|
||||
})
|
||||
idx0++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryLookbackDelta(t *testing.T) {
|
||||
var (
|
||||
load = `load 5m
|
||||
|
@ -3560,7 +3393,7 @@ metric 0 1 2
|
|||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
engine := promqltest.NewTestEngine(false, c.engineLookback, promqltest.DefaultMaxSamplesPerQuery)
|
||||
engine := promqltest.NewTestEngine(t, false, c.engineLookback, promqltest.DefaultMaxSamplesPerQuery)
|
||||
storage := promqltest.LoadedStorage(t, load)
|
||||
t.Cleanup(func() { storage.Close() })
|
||||
|
||||
|
@ -3597,7 +3430,7 @@ histogram {{sum:4 count:4 buckets:[2 2]}} {{sum:6 count:6 buckets:[3 3]}} {{sum:
|
|||
`
|
||||
storage := promqltest.LoadedStorage(t, load)
|
||||
t.Cleanup(func() { storage.Close() })
|
||||
engine := promqltest.NewTestEngine(false, 0, promqltest.DefaultMaxSamplesPerQuery)
|
||||
engine := promqltest.NewTestEngine(t, false, 0, promqltest.DefaultMaxSamplesPerQuery)
|
||||
|
||||
verify := func(t *testing.T, qry promql.Query, expected []histogram.FloatHistogram) {
|
||||
res := qry.Exec(context.Background())
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package promql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
@ -130,10 +131,18 @@ func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNod
|
|||
sampledInterval := float64(lastT-firstT) / 1000
|
||||
averageDurationBetweenSamples := sampledInterval / float64(numSamplesMinusOne)
|
||||
|
||||
// If the first/last samples are close to the boundaries of the range,
|
||||
// extrapolate the result. This is as we expect that another sample
|
||||
// will exist given the spacing between samples we've seen thus far,
|
||||
// with an allowance for noise.
|
||||
// If samples are close enough to the (lower or upper) boundary of the
|
||||
// range, we extrapolate the rate all the way to the boundary in
|
||||
// question. "Close enough" is defined as "up to 10% more than the
|
||||
// average duration between samples within the range", see
|
||||
// extrapolationThreshold below. Essentially, we are assuming a more or
|
||||
// less regular spacing between samples, and if we don't see a sample
|
||||
// where we would expect one, we assume the series does not cover the
|
||||
// whole range, but starts and/or ends within the range. We still
|
||||
// extrapolate the rate in this case, but not all the way to the
|
||||
// boundary, but only by half of the average duration between samples
|
||||
// (which is our guess for where the series actually starts or ends).
|
||||
|
||||
extrapolationThreshold := averageDurationBetweenSamples * 1.1
|
||||
extrapolateToInterval := sampledInterval
|
||||
|
||||
|
@ -483,9 +492,13 @@ func funcClamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper
|
|||
return enh.Out, nil
|
||||
}
|
||||
for _, el := range vec {
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
el.Metric = el.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: el.Metric.DropMetricName(),
|
||||
F: math.Max(minVal, math.Min(maxVal, el.F)),
|
||||
Metric: el.Metric,
|
||||
F: math.Max(minVal, math.Min(maxVal, el.F)),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -496,9 +509,13 @@ func funcClampMax(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel
|
|||
vec := vals[0].(Vector)
|
||||
maxVal := vals[1].(Vector)[0].F
|
||||
for _, el := range vec {
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
el.Metric = el.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: el.Metric.DropMetricName(),
|
||||
F: math.Min(maxVal, el.F),
|
||||
Metric: el.Metric,
|
||||
F: math.Min(maxVal, el.F),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -509,9 +526,13 @@ func funcClampMin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel
|
|||
vec := vals[0].(Vector)
|
||||
minVal := vals[1].(Vector)[0].F
|
||||
for _, el := range vec {
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
el.Metric = el.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: el.Metric.DropMetricName(),
|
||||
F: math.Max(minVal, el.F),
|
||||
Metric: el.Metric,
|
||||
F: math.Max(minVal, el.F),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -532,8 +553,9 @@ func funcRound(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper
|
|||
for _, el := range vec {
|
||||
f := math.Floor(el.F*toNearestInverse+0.5) / toNearestInverse
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: el.Metric.DropMetricName(),
|
||||
F: f,
|
||||
Metric: el.Metric,
|
||||
F: f,
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -882,9 +904,13 @@ func funcPresentOverTime(vals []parser.Value, args parser.Expressions, enh *Eval
|
|||
func simpleFunc(vals []parser.Value, enh *EvalNodeHelper, f func(float64) float64) Vector {
|
||||
for _, el := range vals[0].(Vector) {
|
||||
if el.H == nil { // Process only float samples.
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
el.Metric = el.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: el.Metric.DropMetricName(),
|
||||
F: f(el.F),
|
||||
Metric: el.Metric,
|
||||
F: f(el.F),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1028,9 +1054,13 @@ func funcSgn(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper)
|
|||
func funcTimestamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
vec := vals[0].(Vector)
|
||||
for _, el := range vec {
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
el.Metric = el.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: el.Metric.DropMetricName(),
|
||||
F: float64(el.T) / 1000,
|
||||
Metric: el.Metric,
|
||||
F: float64(el.T) / 1000,
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -1137,9 +1167,13 @@ func funcHistogramCount(vals []parser.Value, args parser.Expressions, enh *EvalN
|
|||
if sample.H == nil {
|
||||
continue
|
||||
}
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
sample.Metric = sample.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: sample.Metric.DropMetricName(),
|
||||
F: sample.H.Count,
|
||||
Metric: sample.Metric,
|
||||
F: sample.H.Count,
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -1154,9 +1188,13 @@ func funcHistogramSum(vals []parser.Value, args parser.Expressions, enh *EvalNod
|
|||
if sample.H == nil {
|
||||
continue
|
||||
}
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
sample.Metric = sample.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: sample.Metric.DropMetricName(),
|
||||
F: sample.H.Sum,
|
||||
Metric: sample.Metric,
|
||||
F: sample.H.Sum,
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -1171,9 +1209,13 @@ func funcHistogramAvg(vals []parser.Value, args parser.Expressions, enh *EvalNod
|
|||
if sample.H == nil {
|
||||
continue
|
||||
}
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
sample.Metric = sample.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: sample.Metric.DropMetricName(),
|
||||
F: sample.H.Sum / sample.H.Count,
|
||||
Metric: sample.Metric,
|
||||
F: sample.H.Sum / sample.H.Count,
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -1210,9 +1252,13 @@ func funcHistogramStdDev(vals []parser.Value, args parser.Expressions, enh *Eval
|
|||
}
|
||||
variance += cVariance
|
||||
variance /= sample.H.Count
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
sample.Metric = sample.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: sample.Metric.DropMetricName(),
|
||||
F: math.Sqrt(variance),
|
||||
Metric: sample.Metric,
|
||||
F: math.Sqrt(variance),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -1249,9 +1295,13 @@ func funcHistogramStdVar(vals []parser.Value, args parser.Expressions, enh *Eval
|
|||
}
|
||||
variance += cVariance
|
||||
variance /= sample.H.Count
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
sample.Metric = sample.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: sample.Metric.DropMetricName(),
|
||||
F: variance,
|
||||
Metric: sample.Metric,
|
||||
F: variance,
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -1268,9 +1318,13 @@ func funcHistogramFraction(vals []parser.Value, args parser.Expressions, enh *Ev
|
|||
if sample.H == nil {
|
||||
continue
|
||||
}
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
sample.Metric = sample.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: sample.Metric.DropMetricName(),
|
||||
F: histogramFraction(lower, upper, sample.H),
|
||||
Metric: sample.Metric,
|
||||
F: histogramFraction(lower, upper, sample.H),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
|
@ -1338,9 +1392,13 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev
|
|||
continue
|
||||
}
|
||||
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
sample.Metric = sample.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: sample.Metric.DropMetricName(),
|
||||
F: histogramQuantile(q, sample.H),
|
||||
Metric: sample.Metric,
|
||||
F: histogramQuantile(q, sample.H),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1414,7 +1472,7 @@ func funcChanges(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelp
|
|||
}
|
||||
|
||||
// label_replace function operates only on series; does not look at timestamps or values.
|
||||
func (ev *evaluator) evalLabelReplace(args parser.Expressions) (parser.Value, annotations.Annotations) {
|
||||
func (ev *evaluator) evalLabelReplace(ctx context.Context, args parser.Expressions) (parser.Value, annotations.Annotations) {
|
||||
var (
|
||||
dst = stringFromArg(args[1])
|
||||
repl = stringFromArg(args[2])
|
||||
|
@ -1430,7 +1488,7 @@ func (ev *evaluator) evalLabelReplace(args parser.Expressions) (parser.Value, an
|
|||
panic(fmt.Errorf("invalid destination label name in label_replace(): %s", dst))
|
||||
}
|
||||
|
||||
val, ws := ev.eval(args[0])
|
||||
val, ws := ev.eval(ctx, args[0])
|
||||
matrix := val.(Matrix)
|
||||
lb := labels.NewBuilder(labels.EmptyLabels())
|
||||
|
||||
|
@ -1442,6 +1500,11 @@ func (ev *evaluator) evalLabelReplace(args parser.Expressions) (parser.Value, an
|
|||
lb.Reset(el.Metric)
|
||||
lb.Set(dst, string(res))
|
||||
matrix[i].Metric = lb.Labels()
|
||||
if dst == model.MetricNameLabel {
|
||||
matrix[i].DropName = false
|
||||
} else {
|
||||
matrix[i].DropName = el.DropName
|
||||
}
|
||||
}
|
||||
}
|
||||
if matrix.ContainsSameLabelset() {
|
||||
|
@ -1466,7 +1529,7 @@ func funcVector(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelpe
|
|||
}
|
||||
|
||||
// label_join function operates only on series; does not look at timestamps or values.
|
||||
func (ev *evaluator) evalLabelJoin(args parser.Expressions) (parser.Value, annotations.Annotations) {
|
||||
func (ev *evaluator) evalLabelJoin(ctx context.Context, args parser.Expressions) (parser.Value, annotations.Annotations) {
|
||||
var (
|
||||
dst = stringFromArg(args[1])
|
||||
sep = stringFromArg(args[2])
|
||||
|
@ -1483,7 +1546,7 @@ func (ev *evaluator) evalLabelJoin(args parser.Expressions) (parser.Value, annot
|
|||
panic(fmt.Errorf("invalid destination label name in label_join(): %s", dst))
|
||||
}
|
||||
|
||||
val, ws := ev.eval(args[0])
|
||||
val, ws := ev.eval(ctx, args[0])
|
||||
matrix := val.(Matrix)
|
||||
srcVals := make([]string, len(srcLabels))
|
||||
lb := labels.NewBuilder(labels.EmptyLabels())
|
||||
|
@ -1496,6 +1559,12 @@ func (ev *evaluator) evalLabelJoin(args parser.Expressions) (parser.Value, annot
|
|||
lb.Reset(el.Metric)
|
||||
lb.Set(dst, strval)
|
||||
matrix[i].Metric = lb.Labels()
|
||||
|
||||
if dst == model.MetricNameLabel {
|
||||
matrix[i].DropName = false
|
||||
} else {
|
||||
matrix[i].DropName = el.DropName
|
||||
}
|
||||
}
|
||||
|
||||
return matrix, ws
|
||||
|
@ -1518,9 +1587,13 @@ func dateWrapper(vals []parser.Value, enh *EvalNodeHelper, f func(time.Time) flo
|
|||
|
||||
for _, el := range vals[0].(Vector) {
|
||||
t := time.Unix(int64(el.F), 0).UTC()
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
el.Metric = el.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: el.Metric.DropMetricName(),
|
||||
F: f(t),
|
||||
Metric: el.Metric,
|
||||
F: f(t),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/prometheus/prometheus/model/timestamp"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
"github.com/prometheus/prometheus/promql/promqltest"
|
||||
"github.com/prometheus/prometheus/util/teststorage"
|
||||
)
|
||||
|
||||
|
@ -39,7 +40,7 @@ func TestDeriv(t *testing.T) {
|
|||
MaxSamples: 10000,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
|
||||
a := storage.Appender(context.Background())
|
||||
|
||||
|
|
|
@ -617,6 +617,16 @@ func lexBuckets(l *Lexer) stateFn {
|
|||
l.bracketOpen = false
|
||||
l.emit(RIGHT_BRACKET)
|
||||
return lexHistogram
|
||||
case isAlpha(r):
|
||||
// Current word is Inf or NaN.
|
||||
word := l.input[l.start:l.pos]
|
||||
if desc, ok := key[strings.ToLower(word)]; ok {
|
||||
if desc == NUMBER {
|
||||
l.emit(desc)
|
||||
return lexStatements
|
||||
}
|
||||
}
|
||||
return lexBuckets
|
||||
default:
|
||||
return l.errorf("invalid character in buckets description: %q", r)
|
||||
}
|
||||
|
|
|
@ -639,6 +639,29 @@ var tests = []struct {
|
|||
},
|
||||
seriesDesc: true,
|
||||
},
|
||||
{
|
||||
input: `{} {{buckets: [Inf NaN] schema:1}}`,
|
||||
expected: []Item{
|
||||
{LEFT_BRACE, 0, `{`},
|
||||
{RIGHT_BRACE, 1, `}`},
|
||||
{SPACE, 2, ` `},
|
||||
{OPEN_HIST, 3, `{{`},
|
||||
{BUCKETS_DESC, 5, `buckets`},
|
||||
{COLON, 12, `:`},
|
||||
{SPACE, 13, ` `},
|
||||
{LEFT_BRACKET, 14, `[`},
|
||||
{NUMBER, 15, `Inf`},
|
||||
{SPACE, 18, ` `},
|
||||
{NUMBER, 19, `NaN`},
|
||||
{RIGHT_BRACKET, 22, `]`},
|
||||
{SPACE, 23, ` `},
|
||||
{SCHEMA_DESC, 24, `schema`},
|
||||
{COLON, 30, `:`},
|
||||
{NUMBER, 31, `1`},
|
||||
{CLOSE_HIST, 32, `}}`},
|
||||
},
|
||||
seriesDesc: true,
|
||||
},
|
||||
{ // Series with sum as -Inf and count as NaN.
|
||||
input: `{} {{buckets: [5 10 7] sum:Inf count:NaN}}`,
|
||||
expected: []Item{
|
||||
|
|
|
@ -72,6 +72,11 @@ func (node *AggregateExpr) String() string {
|
|||
return aggrString
|
||||
}
|
||||
|
||||
func (node *AggregateExpr) ShortString() string {
|
||||
aggrString := node.getAggOpStr()
|
||||
return aggrString
|
||||
}
|
||||
|
||||
func (node *AggregateExpr) getAggOpStr() string {
|
||||
aggrString := node.Op.String()
|
||||
|
||||
|
@ -95,14 +100,20 @@ func joinLabels(ss []string) string {
|
|||
return strings.Join(ss, ", ")
|
||||
}
|
||||
|
||||
func (node *BinaryExpr) String() string {
|
||||
returnBool := ""
|
||||
func (node *BinaryExpr) returnBool() string {
|
||||
if node.ReturnBool {
|
||||
returnBool = " bool"
|
||||
return " bool"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (node *BinaryExpr) String() string {
|
||||
matching := node.getMatchingStr()
|
||||
return fmt.Sprintf("%s %s%s%s %s", node.LHS, node.Op, returnBool, matching, node.RHS)
|
||||
return fmt.Sprintf("%s %s%s%s %s", node.LHS, node.Op, node.returnBool(), matching, node.RHS)
|
||||
}
|
||||
|
||||
func (node *BinaryExpr) ShortString() string {
|
||||
return fmt.Sprintf("%s%s%s", node.Op, node.returnBool(), node.getMatchingStr())
|
||||
}
|
||||
|
||||
func (node *BinaryExpr) getMatchingStr() string {
|
||||
|
@ -130,9 +141,13 @@ func (node *Call) String() string {
|
|||
return fmt.Sprintf("%s(%s)", node.Func.Name, node.Args)
|
||||
}
|
||||
|
||||
func (node *MatrixSelector) String() string {
|
||||
func (node *Call) ShortString() string {
|
||||
return node.Func.Name
|
||||
}
|
||||
|
||||
func (node *MatrixSelector) atOffset() (string, string) {
|
||||
// Copy the Vector selector before changing the offset
|
||||
vecSelector := *node.VectorSelector.(*VectorSelector)
|
||||
vecSelector := node.VectorSelector.(*VectorSelector)
|
||||
offset := ""
|
||||
switch {
|
||||
case vecSelector.OriginalOffset > time.Duration(0):
|
||||
|
@ -149,7 +164,13 @@ func (node *MatrixSelector) String() string {
|
|||
case vecSelector.StartOrEnd == END:
|
||||
at = " @ end()"
|
||||
}
|
||||
return at, offset
|
||||
}
|
||||
|
||||
func (node *MatrixSelector) String() string {
|
||||
at, offset := node.atOffset()
|
||||
// Copy the Vector selector before changing the offset
|
||||
vecSelector := *node.VectorSelector.(*VectorSelector)
|
||||
// Do not print the @ and offset twice.
|
||||
offsetVal, atVal, preproc := vecSelector.OriginalOffset, vecSelector.Timestamp, vecSelector.StartOrEnd
|
||||
vecSelector.OriginalOffset = 0
|
||||
|
@ -163,10 +184,19 @@ func (node *MatrixSelector) String() string {
|
|||
return str
|
||||
}
|
||||
|
||||
func (node *MatrixSelector) ShortString() string {
|
||||
at, offset := node.atOffset()
|
||||
return fmt.Sprintf("[%s]%s%s", model.Duration(node.Range), at, offset)
|
||||
}
|
||||
|
||||
func (node *SubqueryExpr) String() string {
|
||||
return fmt.Sprintf("%s%s", node.Expr.String(), node.getSubqueryTimeSuffix())
|
||||
}
|
||||
|
||||
func (node *SubqueryExpr) ShortString() string {
|
||||
return node.getSubqueryTimeSuffix()
|
||||
}
|
||||
|
||||
// getSubqueryTimeSuffix returns the '[<range>:<step>] @ <timestamp> offset <offset>' suffix of the subquery.
|
||||
func (node *SubqueryExpr) getSubqueryTimeSuffix() string {
|
||||
step := ""
|
||||
|
@ -208,6 +238,10 @@ func (node *UnaryExpr) String() string {
|
|||
return fmt.Sprintf("%s%s", node.Op, node.Expr)
|
||||
}
|
||||
|
||||
func (node *UnaryExpr) ShortString() string {
|
||||
return node.Op.String()
|
||||
}
|
||||
|
||||
func (node *VectorSelector) String() string {
|
||||
var labelStrings []string
|
||||
if len(node.LabelMatchers) > 1 {
|
||||
|
|
|
@ -28,12 +28,12 @@ import (
|
|||
"github.com/prometheus/prometheus/util/teststorage"
|
||||
)
|
||||
|
||||
func newTestEngine() *promql.Engine {
|
||||
return promqltest.NewTestEngine(false, 0, promqltest.DefaultMaxSamplesPerQuery)
|
||||
func newTestEngine(t *testing.T) *promql.Engine {
|
||||
return promqltest.NewTestEngine(t, false, 0, promqltest.DefaultMaxSamplesPerQuery)
|
||||
}
|
||||
|
||||
func TestEvaluations(t *testing.T) {
|
||||
promqltest.RunBuiltinTests(t, newTestEngine())
|
||||
promqltest.RunBuiltinTests(t, newTestEngine(t))
|
||||
}
|
||||
|
||||
// Run a lot of queries at the same time, to check for race conditions.
|
||||
|
@ -48,7 +48,7 @@ func TestConcurrentRangeQueries(t *testing.T) {
|
|||
}
|
||||
// Enable experimental functions testing
|
||||
parser.EnableExperimentalFunctions = true
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
|
||||
const interval = 10000 // 10s interval.
|
||||
// A day of data plus 10k steps.
|
||||
|
|
|
@ -79,8 +79,9 @@ func LoadedStorage(t testutil.T, input string) *teststorage.TestStorage {
|
|||
return test.storage
|
||||
}
|
||||
|
||||
func NewTestEngine(enablePerStepStats bool, lookbackDelta time.Duration, maxSamples int) *promql.Engine {
|
||||
return promql.NewEngine(promql.EngineOpts{
|
||||
// NewTestEngine creates a promql.Engine with enablePerStepStats, lookbackDelta and maxSamples, and returns it.
|
||||
func NewTestEngine(tb testing.TB, enablePerStepStats bool, lookbackDelta time.Duration, maxSamples int) *promql.Engine {
|
||||
return NewTestEngineWithOpts(tb, promql.EngineOpts{
|
||||
Logger: nil,
|
||||
Reg: nil,
|
||||
MaxSamples: maxSamples,
|
||||
|
@ -90,9 +91,20 @@ func NewTestEngine(enablePerStepStats bool, lookbackDelta time.Duration, maxSamp
|
|||
EnableNegativeOffset: true,
|
||||
EnablePerStepStats: enablePerStepStats,
|
||||
LookbackDelta: lookbackDelta,
|
||||
EnableDelayedNameRemoval: true,
|
||||
})
|
||||
}
|
||||
|
||||
// NewTestEngineWithOpts creates a promql.Engine with opts and returns it.
|
||||
func NewTestEngineWithOpts(tb testing.TB, opts promql.EngineOpts) *promql.Engine {
|
||||
tb.Helper()
|
||||
ng := promql.NewEngine(opts)
|
||||
tb.Cleanup(func() {
|
||||
require.NoError(tb, ng.Close())
|
||||
})
|
||||
return ng
|
||||
}
|
||||
|
||||
// RunBuiltinTests runs an acceptance test suite against the provided engine.
|
||||
func RunBuiltinTests(t TBRun, engine promql.QueryEngine) {
|
||||
t.Cleanup(func() { parser.EnableExperimentalFunctions = false })
|
||||
|
@ -650,8 +662,9 @@ type evalCmd struct {
|
|||
expectedFailMessage string
|
||||
expectedFailRegexp *regexp.Regexp
|
||||
|
||||
metrics map[uint64]labels.Labels
|
||||
expected map[uint64]entry
|
||||
metrics map[uint64]labels.Labels
|
||||
expectScalar bool
|
||||
expected map[uint64]entry
|
||||
}
|
||||
|
||||
type entry struct {
|
||||
|
@ -695,12 +708,15 @@ func (ev *evalCmd) String() string {
|
|||
// expect adds a sequence of values to the set of expected
|
||||
// results for the query.
|
||||
func (ev *evalCmd) expect(pos int, vals ...parser.SequenceValue) {
|
||||
ev.expectScalar = true
|
||||
ev.expected[0] = entry{pos: pos, vals: vals}
|
||||
}
|
||||
|
||||
// expectMetric adds a new metric with a sequence of values to the set of expected
|
||||
// results for the query.
|
||||
func (ev *evalCmd) expectMetric(pos int, m labels.Labels, vals ...parser.SequenceValue) {
|
||||
ev.expectScalar = false
|
||||
|
||||
h := m.Hash()
|
||||
ev.metrics[h] = m
|
||||
ev.expected[h] = entry{pos: pos, vals: vals}
|
||||
|
@ -714,6 +730,10 @@ func (ev *evalCmd) compareResult(result parser.Value) error {
|
|||
return fmt.Errorf("expected ordered result, but query returned a matrix")
|
||||
}
|
||||
|
||||
if ev.expectScalar {
|
||||
return fmt.Errorf("expected scalar result, but got matrix %s", val.String())
|
||||
}
|
||||
|
||||
if err := assertMatrixSorted(val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -782,6 +802,10 @@ func (ev *evalCmd) compareResult(result parser.Value) error {
|
|||
}
|
||||
|
||||
case promql.Vector:
|
||||
if ev.expectScalar {
|
||||
return fmt.Errorf("expected scalar result, but got vector %s", val.String())
|
||||
}
|
||||
|
||||
seen := map[uint64]bool{}
|
||||
for pos, v := range val {
|
||||
fp := v.Metric.Hash()
|
||||
|
@ -820,15 +844,15 @@ func (ev *evalCmd) compareResult(result parser.Value) error {
|
|||
}
|
||||
|
||||
case promql.Scalar:
|
||||
if len(ev.expected) != 1 {
|
||||
return fmt.Errorf("expected vector result, but got scalar %s", val.String())
|
||||
if !ev.expectScalar {
|
||||
return fmt.Errorf("expected vector or matrix result, but got %s", val.String())
|
||||
}
|
||||
exp0 := ev.expected[0].vals[0]
|
||||
if exp0.Histogram != nil {
|
||||
return fmt.Errorf("expected Histogram %v but got scalar %s", exp0.Histogram.TestExpression(), val.String())
|
||||
return fmt.Errorf("expected histogram %v but got %s", exp0.Histogram.TestExpression(), val.String())
|
||||
}
|
||||
if !almost.Equal(exp0.Value, val.V, defaultEpsilon) {
|
||||
return fmt.Errorf("expected Scalar %v but got %v", val.V, exp0.Value)
|
||||
return fmt.Errorf("expected scalar %v but got %v", exp0.Value, val.V)
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -1362,6 +1386,7 @@ func (ll *LazyLoader) clear() error {
|
|||
NoStepSubqueryIntervalFn: func(int64) int64 { return durationMilliseconds(ll.SubqueryInterval) },
|
||||
EnableAtModifier: ll.opts.EnableAtModifier,
|
||||
EnableNegativeOffset: ll.opts.EnableNegativeOffset,
|
||||
EnableDelayedNameRemoval: true,
|
||||
}
|
||||
|
||||
ll.queryEngine = promql.NewEngine(opts)
|
||||
|
@ -1422,7 +1447,11 @@ func (ll *LazyLoader) Storage() storage.Storage {
|
|||
// Close closes resources associated with the LazyLoader.
|
||||
func (ll *LazyLoader) Close() error {
|
||||
ll.cancelCtx()
|
||||
return ll.storage.Close()
|
||||
err := ll.queryEngine.Close()
|
||||
if sErr := ll.storage.Close(); sErr != nil {
|
||||
return errors.Join(sErr, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func makeInt64Pointer(val int64) *int64 {
|
||||
|
|
|
@ -554,11 +554,48 @@ eval range from 0 to 5m step 5m testmetric
|
|||
`,
|
||||
expectedError: `error in eval testmetric (line 5): expected float value at index 0 for {__name__="testmetric"} to have timestamp 300000, but it had timestamp 0 (result has 1 float point [3 @[0]] and 1 histogram point [{count:0, sum:0} @[300000]])`,
|
||||
},
|
||||
"instant query with expected scalar result": {
|
||||
input: `
|
||||
eval instant at 1m 3
|
||||
3
|
||||
`,
|
||||
},
|
||||
"instant query with unexpected scalar result": {
|
||||
input: `
|
||||
eval instant at 1m 3
|
||||
2
|
||||
`,
|
||||
expectedError: `error in eval 3 (line 2): expected scalar 2 but got 3`,
|
||||
},
|
||||
"instant query that returns a scalar but expects a vector": {
|
||||
input: `
|
||||
eval instant at 1m 3
|
||||
{} 3
|
||||
`,
|
||||
expectedError: `error in eval 3 (line 2): expected vector or matrix result, but got scalar: 3 @[60000]`,
|
||||
},
|
||||
"instant query that returns a vector but expects a scalar": {
|
||||
input: `
|
||||
eval instant at 1m vector(3)
|
||||
3
|
||||
`,
|
||||
expectedError: `error in eval vector(3) (line 2): expected scalar result, but got vector {} => 3 @[60000]`,
|
||||
},
|
||||
"range query that returns a matrix but expects a scalar": {
|
||||
input: `
|
||||
eval range from 0 to 1m step 30s vector(3)
|
||||
3
|
||||
`,
|
||||
expectedError: `error in eval vector(3) (line 2): expected scalar result, but got matrix {} =>
|
||||
3 @[0]
|
||||
3 @[30000]
|
||||
3 @[60000]`,
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
err := runTest(t, testCase.input, NewTestEngine(false, 0, DefaultMaxSamplesPerQuery))
|
||||
err := runTest(t, testCase.input, NewTestEngine(t, false, 0, DefaultMaxSamplesPerQuery))
|
||||
|
||||
if testCase.expectedError == "" {
|
||||
require.NoError(t, err)
|
||||
|
|
84
promql/promqltest/testdata/name_label_dropping.test
vendored
Normal file
84
promql/promqltest/testdata/name_label_dropping.test
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
# Test for __name__ label drop.
|
||||
load 5m
|
||||
metric{env="1"} 0 60 120
|
||||
another_metric{env="1"} 60 120 180
|
||||
|
||||
# Does not drop __name__ for vector selector
|
||||
eval instant at 15m metric{env="1"}
|
||||
metric{env="1"} 120
|
||||
|
||||
# Drops __name__ for unary operators
|
||||
eval instant at 15m -metric
|
||||
{env="1"} -120
|
||||
|
||||
# Drops __name__ for binary operators
|
||||
eval instant at 15m metric + another_metric
|
||||
{env="1"} 300
|
||||
|
||||
# Does not drop __name__ for binary comparison operators
|
||||
eval instant at 15m metric <= another_metric
|
||||
metric{env="1"} 120
|
||||
|
||||
# Drops __name__ for binary comparison operators with "bool" modifier
|
||||
eval instant at 15m metric <= bool another_metric
|
||||
{env="1"} 1
|
||||
|
||||
# Drops __name__ for vector-scalar operations
|
||||
eval instant at 15m metric * 2
|
||||
{env="1"} 240
|
||||
|
||||
# Drops __name__ for instant-vector functions
|
||||
eval instant at 15m clamp(metric, 0, 100)
|
||||
{env="1"} 100
|
||||
|
||||
# Drops __name__ for range-vector functions
|
||||
eval instant at 15m rate(metric{env="1"}[10m])
|
||||
{env="1"} 0.2
|
||||
|
||||
# Does not drop __name__ for last_over_time function
|
||||
eval instant at 15m last_over_time(metric{env="1"}[10m])
|
||||
metric{env="1"} 120
|
||||
|
||||
# Drops name for other _over_time functions
|
||||
eval instant at 15m max_over_time(metric{env="1"}[10m])
|
||||
{env="1"} 120
|
||||
|
||||
# Allows relabeling (to-be-dropped) __name__ via label_replace
|
||||
eval instant at 15m label_replace(rate({env="1"}[10m]), "my_name", "rate_$1", "__name__", "(.+)")
|
||||
{my_name="rate_metric", env="1"} 0.2
|
||||
{my_name="rate_another_metric", env="1"} 0.2
|
||||
|
||||
# Allows preserving __name__ via label_replace
|
||||
eval instant at 15m label_replace(rate({env="1"}[10m]), "__name__", "rate_$1", "__name__", "(.+)")
|
||||
rate_metric{env="1"} 0.2
|
||||
rate_another_metric{env="1"} 0.2
|
||||
|
||||
# Allows relabeling (to-be-dropped) __name__ via label_join
|
||||
eval instant at 15m label_join(rate({env="1"}[10m]), "my_name", "_", "__name__")
|
||||
{my_name="metric", env="1"} 0.2
|
||||
{my_name="another_metric", env="1"} 0.2
|
||||
|
||||
# Allows preserving __name__ via label_join
|
||||
eval instant at 15m label_join(rate({env="1"}[10m]), "__name__", "_", "__name__", "env")
|
||||
metric_1{env="1"} 0.2
|
||||
another_metric_1{env="1"} 0.2
|
||||
|
||||
# Does not drop metric names fro aggregation operators
|
||||
eval instant at 15m sum by (__name__, env) (metric{env="1"})
|
||||
metric{env="1"} 120
|
||||
|
||||
# Aggregation operators by __name__ lead to duplicate labelset errors (aggregation is partitioned by not yet removed __name__ label)
|
||||
# This is an accidental side effect of delayed __name__ label dropping
|
||||
eval_fail instant at 15m sum by (__name__) (rate({env="1"}[10m]))
|
||||
|
||||
# Aggregation operators aggregate metrics with same labelset and to-be-dropped names
|
||||
# This is an accidental side effect of delayed __name__ label dropping
|
||||
eval instant at 15m sum(rate({env="1"}[10m])) by (env)
|
||||
{env="1"} 0.4
|
||||
|
||||
# Aggregationk operators propagate __name__ label dropping information
|
||||
eval instant at 15m topk(10, sum by (__name__, env) (metric{env="1"}))
|
||||
metric{env="1"} 120
|
||||
|
||||
eval instant at 15m topk(10, sum by (__name__, env) (rate(metric{env="1"}[10m])))
|
||||
{env="1"} 0.2
|
|
@ -718,6 +718,52 @@ eval instant at 10m histogram_fraction(-Inf, +Inf, histogram_fraction_4)
|
|||
eval instant at 10m histogram_sum(scalar(histogram_fraction(-Inf, +Inf, sum(histogram_fraction_4))) * histogram_fraction_4)
|
||||
{} 100
|
||||
|
||||
# Apply multiplication and division operator to histogram.
|
||||
load 10m
|
||||
histogram_mul_div {{schema:0 count:21 sum:33 z_bucket:3 z_bucket_w:0.001 buckets:[3 3 3] n_buckets:[3 3 3]}}x1
|
||||
float_series_3 3+0x1
|
||||
float_series_0 0+0x1
|
||||
|
||||
eval instant at 10m histogram_mul_div*3
|
||||
{} {{schema:0 count:63 sum:99 z_bucket:9 z_bucket_w:0.001 buckets:[9 9 9] n_buckets:[9 9 9]}}
|
||||
|
||||
eval instant at 10m 3*histogram_mul_div
|
||||
{} {{schema:0 count:63 sum:99 z_bucket:9 z_bucket_w:0.001 buckets:[9 9 9] n_buckets:[9 9 9]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div*float_series_3
|
||||
{} {{schema:0 count:63 sum:99 z_bucket:9 z_bucket_w:0.001 buckets:[9 9 9] n_buckets:[9 9 9]}}
|
||||
|
||||
eval instant at 10m float_series_3*histogram_mul_div
|
||||
{} {{schema:0 count:63 sum:99 z_bucket:9 z_bucket_w:0.001 buckets:[9 9 9] n_buckets:[9 9 9]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div/3
|
||||
{} {{schema:0 count:7 sum:11 z_bucket:1 z_bucket_w:0.001 buckets:[1 1 1] n_buckets:[1 1 1]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div/float_series_3
|
||||
{} {{schema:0 count:7 sum:11 z_bucket:1 z_bucket_w:0.001 buckets:[1 1 1] n_buckets:[1 1 1]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div*0
|
||||
{} {{schema:0 count:0 sum:0 z_bucket:0 z_bucket_w:0.001 buckets:[0 0 0] n_buckets:[0 0 0]}}
|
||||
|
||||
eval instant at 10m 0*histogram_mul_div
|
||||
{} {{schema:0 count:0 sum:0 z_bucket:0 z_bucket_w:0.001 buckets:[0 0 0] n_buckets:[0 0 0]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div*float_series_0
|
||||
{} {{schema:0 count:0 sum:0 z_bucket:0 z_bucket_w:0.001 buckets:[0 0 0] n_buckets:[0 0 0]}}
|
||||
|
||||
eval instant at 10m float_series_0*histogram_mul_div
|
||||
{} {{schema:0 count:0 sum:0 z_bucket:0 z_bucket_w:0.001 buckets:[0 0 0] n_buckets:[0 0 0]}}
|
||||
|
||||
# TODO: (NeerajGartia21) remove all the histogram buckets in case of division with zero. See: https://github.com/prometheus/prometheus/issues/13934
|
||||
eval instant at 10m histogram_mul_div/0
|
||||
{} {{schema:0 count:Inf sum:Inf z_bucket:Inf z_bucket_w:0.001 buckets:[Inf Inf Inf] n_buckets:[Inf Inf Inf]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div/float_series_0
|
||||
{} {{schema:0 count:Inf sum:Inf z_bucket:Inf z_bucket_w:0.001 buckets:[Inf Inf Inf] n_buckets:[Inf Inf Inf]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div*0/0
|
||||
{} {{schema:0 count:NaN sum:NaN z_bucket:NaN z_bucket_w:0.001 buckets:[NaN NaN NaN] n_buckets:[NaN NaN NaN]}}
|
||||
|
||||
clear
|
||||
|
||||
# Counter reset only noticeable in a single bucket.
|
||||
|
|
|
@ -68,6 +68,9 @@ type Series struct {
|
|||
Metric labels.Labels `json:"metric"`
|
||||
Floats []FPoint `json:"values,omitempty"`
|
||||
Histograms []HPoint `json:"histograms,omitempty"`
|
||||
// DropName is used to indicate whether the __name__ label should be dropped
|
||||
// as part of the query evaluation.
|
||||
DropName bool `json:"-"`
|
||||
}
|
||||
|
||||
func (s Series) String() string {
|
||||
|
@ -194,6 +197,9 @@ type Sample struct {
|
|||
H *histogram.FloatHistogram
|
||||
|
||||
Metric labels.Labels
|
||||
// DropName is used to indicate whether the __name__ label should be dropped
|
||||
// as part of the query evaluation.
|
||||
DropName bool
|
||||
}
|
||||
|
||||
func (s Sample) String() string {
|
||||
|
|
|
@ -36,16 +36,19 @@ import (
|
|||
"github.com/prometheus/prometheus/util/testutil"
|
||||
)
|
||||
|
||||
var testEngine = promql.NewEngine(promql.EngineOpts{
|
||||
Logger: nil,
|
||||
Reg: nil,
|
||||
MaxSamples: 10000,
|
||||
Timeout: 100 * time.Second,
|
||||
NoStepSubqueryIntervalFn: func(int64) int64 { return 60 * 1000 },
|
||||
EnableAtModifier: true,
|
||||
EnableNegativeOffset: true,
|
||||
EnablePerStepStats: true,
|
||||
})
|
||||
func testEngine(tb testing.TB) *promql.Engine {
|
||||
tb.Helper()
|
||||
return promqltest.NewTestEngineWithOpts(tb, promql.EngineOpts{
|
||||
Logger: nil,
|
||||
Reg: nil,
|
||||
MaxSamples: 10000,
|
||||
Timeout: 100 * time.Second,
|
||||
NoStepSubqueryIntervalFn: func(int64) int64 { return 60 * 1000 },
|
||||
EnableAtModifier: true,
|
||||
EnableNegativeOffset: true,
|
||||
EnablePerStepStats: true,
|
||||
})
|
||||
}
|
||||
|
||||
func TestAlertingRuleState(t *testing.T) {
|
||||
tests := []struct {
|
||||
|
@ -225,12 +228,14 @@ func TestAlertingRuleLabelsUpdate(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
ng := testEngine(t)
|
||||
|
||||
baseTime := time.Unix(0, 0)
|
||||
for i, result := range results {
|
||||
t.Logf("case %d", i)
|
||||
evalTime := baseTime.Add(time.Duration(i) * time.Minute)
|
||||
result[0].T = timestamp.FromTime(evalTime)
|
||||
res, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
|
||||
res, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
|
||||
|
@ -247,7 +252,7 @@ func TestAlertingRuleLabelsUpdate(t *testing.T) {
|
|||
testutil.RequireEqual(t, result, filteredRes)
|
||||
}
|
||||
evalTime := baseTime.Add(time.Duration(len(results)) * time.Minute)
|
||||
res, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
|
||||
res, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, res)
|
||||
}
|
||||
|
@ -309,13 +314,15 @@ func TestAlertingRuleExternalLabelsInTemplate(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
ng := testEngine(t)
|
||||
|
||||
evalTime := time.Unix(0, 0)
|
||||
result[0].T = timestamp.FromTime(evalTime)
|
||||
result[1].T = timestamp.FromTime(evalTime)
|
||||
|
||||
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
|
||||
res, err := ruleWithoutExternalLabels.Eval(
|
||||
context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0,
|
||||
context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
for _, smpl := range res {
|
||||
|
@ -329,7 +336,7 @@ func TestAlertingRuleExternalLabelsInTemplate(t *testing.T) {
|
|||
}
|
||||
|
||||
res, err = ruleWithExternalLabels.Eval(
|
||||
context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0,
|
||||
context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
for _, smpl := range res {
|
||||
|
@ -406,9 +413,11 @@ func TestAlertingRuleExternalURLInTemplate(t *testing.T) {
|
|||
result[0].T = timestamp.FromTime(evalTime)
|
||||
result[1].T = timestamp.FromTime(evalTime)
|
||||
|
||||
ng := testEngine(t)
|
||||
|
||||
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
|
||||
res, err := ruleWithoutExternalURL.Eval(
|
||||
context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0,
|
||||
context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
for _, smpl := range res {
|
||||
|
@ -422,7 +431,7 @@ func TestAlertingRuleExternalURLInTemplate(t *testing.T) {
|
|||
}
|
||||
|
||||
res, err = ruleWithExternalURL.Eval(
|
||||
context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0,
|
||||
context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
for _, smpl := range res {
|
||||
|
@ -475,9 +484,11 @@ func TestAlertingRuleEmptyLabelFromTemplate(t *testing.T) {
|
|||
evalTime := time.Unix(0, 0)
|
||||
result[0].T = timestamp.FromTime(evalTime)
|
||||
|
||||
ng := testEngine(t)
|
||||
|
||||
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
|
||||
res, err := rule.Eval(
|
||||
context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0,
|
||||
context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
for _, smpl := range res {
|
||||
|
@ -520,6 +531,8 @@ instance: {{ $v.Labels.instance }}, value: {{ printf "%.0f" $v.Value }};
|
|||
)
|
||||
evalTime := time.Unix(0, 0)
|
||||
|
||||
ng := testEngine(t)
|
||||
|
||||
startQueryCh := make(chan struct{})
|
||||
getDoneCh := make(chan struct{})
|
||||
slowQueryFunc := func(ctx context.Context, q string, ts time.Time) (promql.Vector, error) {
|
||||
|
@ -533,7 +546,7 @@ instance: {{ $v.Labels.instance }}, value: {{ printf "%.0f" $v.Value }};
|
|||
require.Fail(t, "unexpected blocking when template expanding.")
|
||||
}
|
||||
}
|
||||
return EngineQueryFunc(testEngine, storage)(ctx, q, ts)
|
||||
return EngineQueryFunc(ng, storage)(ctx, q, ts)
|
||||
}
|
||||
go func() {
|
||||
<-startQueryCh
|
||||
|
@ -578,7 +591,7 @@ func TestAlertingRuleDuplicate(t *testing.T) {
|
|||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||
defer cancelCtx()
|
||||
|
||||
|
@ -642,9 +655,9 @@ func TestAlertingRuleLimit(t *testing.T) {
|
|||
)
|
||||
|
||||
evalTime := time.Unix(0, 0)
|
||||
|
||||
ng := testEngine(t)
|
||||
for _, test := range tests {
|
||||
switch _, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, test.limit); {
|
||||
switch _, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, test.limit); {
|
||||
case err != nil:
|
||||
require.EqualError(t, err, test.err)
|
||||
case test.err != "":
|
||||
|
@ -866,12 +879,13 @@ func TestKeepFiringFor(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
ng := testEngine(t)
|
||||
baseTime := time.Unix(0, 0)
|
||||
for i, result := range results {
|
||||
t.Logf("case %d", i)
|
||||
evalTime := baseTime.Add(time.Duration(i) * time.Minute)
|
||||
result[0].T = timestamp.FromTime(evalTime)
|
||||
res, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
|
||||
res, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
|
||||
|
@ -888,7 +902,7 @@ func TestKeepFiringFor(t *testing.T) {
|
|||
testutil.RequireEqual(t, result, filteredRes)
|
||||
}
|
||||
evalTime := baseTime.Add(time.Duration(len(results)) * time.Minute)
|
||||
res, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
|
||||
res, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, res)
|
||||
}
|
||||
|
@ -923,9 +937,10 @@ func TestPendingAndKeepFiringFor(t *testing.T) {
|
|||
F: 1,
|
||||
}
|
||||
|
||||
ng := testEngine(t)
|
||||
baseTime := time.Unix(0, 0)
|
||||
result.T = timestamp.FromTime(baseTime)
|
||||
res, err := rule.Eval(context.TODO(), 0, baseTime, EngineQueryFunc(testEngine, storage), nil, 0)
|
||||
res, err := rule.Eval(context.TODO(), 0, baseTime, EngineQueryFunc(ng, storage), nil, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, res, 2)
|
||||
|
@ -940,7 +955,7 @@ func TestPendingAndKeepFiringFor(t *testing.T) {
|
|||
}
|
||||
|
||||
evalTime := baseTime.Add(time.Minute)
|
||||
res, err = rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
|
||||
res, err = rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, res)
|
||||
}
|
||||
|
|
|
@ -158,12 +158,13 @@ func TestAlertingRule(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
ng := testEngine(t)
|
||||
for i, test := range tests {
|
||||
t.Logf("case %d", i)
|
||||
|
||||
evalTime := baseTime.Add(test.time)
|
||||
|
||||
res, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
|
||||
res, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
|
||||
|
@ -299,6 +300,7 @@ func TestForStateAddSamples(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
ng := testEngine(t)
|
||||
var forState float64
|
||||
for i, test := range tests {
|
||||
t.Logf("case %d", i)
|
||||
|
@ -311,7 +313,7 @@ func TestForStateAddSamples(t *testing.T) {
|
|||
forState = float64(value.StaleNaN)
|
||||
}
|
||||
|
||||
res, err := rule.Eval(context.TODO(), queryOffset, evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
|
||||
res, err := rule.Eval(context.TODO(), queryOffset, evalTime, EngineQueryFunc(ng, storage), nil, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
var filteredRes promql.Vector // After removing 'ALERTS' samples.
|
||||
|
@ -366,8 +368,9 @@ func TestForStateRestore(t *testing.T) {
|
|||
expr, err := parser.ParseExpr(`http_requests{group="canary", job="app-server"} < 100`)
|
||||
require.NoError(t, err)
|
||||
|
||||
ng := testEngine(t)
|
||||
opts := &ManagerOptions{
|
||||
QueryFunc: EngineQueryFunc(testEngine, storage),
|
||||
QueryFunc: EngineQueryFunc(ng, storage),
|
||||
Appendable: storage,
|
||||
Queryable: storage,
|
||||
Context: context.Background(),
|
||||
|
@ -538,7 +541,7 @@ func TestStaleness(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(engineOpts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, engineOpts)
|
||||
opts := &ManagerOptions{
|
||||
QueryFunc: EngineQueryFunc(engine, st),
|
||||
Appendable: st,
|
||||
|
@ -772,7 +775,7 @@ func TestUpdate(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
ruleManager := NewManager(&ManagerOptions{
|
||||
Appendable: st,
|
||||
Queryable: st,
|
||||
|
@ -910,7 +913,7 @@ func TestNotify(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(engineOpts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, engineOpts)
|
||||
var lastNotified []*Alert
|
||||
notifyFunc := func(ctx context.Context, expr string, alerts ...*Alert) {
|
||||
lastNotified = alerts
|
||||
|
@ -985,7 +988,7 @@ func TestMetricsUpdate(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
ruleManager := NewManager(&ManagerOptions{
|
||||
Appendable: storage,
|
||||
Queryable: storage,
|
||||
|
@ -1059,7 +1062,7 @@ func TestGroupStalenessOnRemoval(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
ruleManager := NewManager(&ManagerOptions{
|
||||
Appendable: storage,
|
||||
Queryable: storage,
|
||||
|
@ -1136,7 +1139,7 @@ func TestMetricsStalenessOnManagerShutdown(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
ruleManager := NewManager(&ManagerOptions{
|
||||
Appendable: storage,
|
||||
Queryable: storage,
|
||||
|
@ -1238,7 +1241,7 @@ func TestRuleHealthUpdates(t *testing.T) {
|
|||
MaxSamples: 10,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
engine := promql.NewEngine(engineOpts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, engineOpts)
|
||||
opts := &ManagerOptions{
|
||||
QueryFunc: EngineQueryFunc(engine, st),
|
||||
Appendable: st,
|
||||
|
@ -1335,9 +1338,10 @@ func TestRuleGroupEvalIterationFunc(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
ng := testEngine(t)
|
||||
testFunc := func(tst testInput) {
|
||||
opts := &ManagerOptions{
|
||||
QueryFunc: EngineQueryFunc(testEngine, storage),
|
||||
QueryFunc: EngineQueryFunc(ng, storage),
|
||||
Appendable: storage,
|
||||
Queryable: storage,
|
||||
Context: context.Background(),
|
||||
|
@ -1421,8 +1425,9 @@ func TestNativeHistogramsInRecordingRules(t *testing.T) {
|
|||
}
|
||||
require.NoError(t, app.Commit())
|
||||
|
||||
ng := testEngine(t)
|
||||
opts := &ManagerOptions{
|
||||
QueryFunc: EngineQueryFunc(testEngine, storage),
|
||||
QueryFunc: EngineQueryFunc(ng, storage),
|
||||
Appendable: storage,
|
||||
Queryable: storage,
|
||||
Context: context.Background(),
|
||||
|
|
|
@ -123,10 +123,11 @@ func TestRuleEval(t *testing.T) {
|
|||
storage := setUpRuleEvalTest(t)
|
||||
t.Cleanup(func() { storage.Close() })
|
||||
|
||||
ng := testEngine(t)
|
||||
for _, scenario := range ruleEvalTestScenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
rule := NewRecordingRule("test_rule", scenario.expr, scenario.ruleLabels)
|
||||
result, err := rule.Eval(context.TODO(), 0, ruleEvaluationTime, EngineQueryFunc(testEngine, storage), nil, 0)
|
||||
result, err := rule.Eval(context.TODO(), 0, ruleEvaluationTime, EngineQueryFunc(ng, storage), nil, 0)
|
||||
require.NoError(t, err)
|
||||
testutil.RequireEqual(t, scenario.expected, result)
|
||||
})
|
||||
|
@ -137,6 +138,7 @@ func BenchmarkRuleEval(b *testing.B) {
|
|||
storage := setUpRuleEvalTest(b)
|
||||
b.Cleanup(func() { storage.Close() })
|
||||
|
||||
ng := testEngine(b)
|
||||
for _, scenario := range ruleEvalTestScenarios {
|
||||
b.Run(scenario.name, func(b *testing.B) {
|
||||
rule := NewRecordingRule("test_rule", scenario.expr, scenario.ruleLabels)
|
||||
|
@ -144,7 +146,7 @@ func BenchmarkRuleEval(b *testing.B) {
|
|||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := rule.Eval(context.TODO(), 0, ruleEvaluationTime, EngineQueryFunc(testEngine, storage), nil, 0)
|
||||
_, err := rule.Eval(context.TODO(), 0, ruleEvaluationTime, EngineQueryFunc(ng, storage), nil, 0)
|
||||
if err != nil {
|
||||
require.NoError(b, err)
|
||||
}
|
||||
|
@ -165,7 +167,7 @@ func TestRuleEvalDuplicate(t *testing.T) {
|
|||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
engine := promql.NewEngine(opts)
|
||||
engine := promqltest.NewTestEngineWithOpts(t, opts)
|
||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||
defer cancelCtx()
|
||||
|
||||
|
@ -212,10 +214,11 @@ func TestRecordingRuleLimit(t *testing.T) {
|
|||
labels.FromStrings("test", "test"),
|
||||
)
|
||||
|
||||
ng := testEngine(t)
|
||||
evalTime := time.Unix(0, 0)
|
||||
|
||||
for _, test := range tests {
|
||||
switch _, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(testEngine, storage), nil, test.limit); {
|
||||
switch _, err := rule.Eval(context.TODO(), 0, evalTime, EngineQueryFunc(ng, storage), nil, test.limit); {
|
||||
case err != nil:
|
||||
require.EqualError(t, err, test.err)
|
||||
case test.err != "":
|
||||
|
|
|
@ -1178,7 +1178,7 @@ scrape_configs:
|
|||
)
|
||||
}
|
||||
|
||||
// TestOnlyStaleTargetsAreDropped makes sure that when a job has multiple providers, when aone of them should no,
|
||||
// TestOnlyStaleTargetsAreDropped makes sure that when a job has multiple providers, when one of them should no
|
||||
// longer discover targets, only the stale targets of that provier are dropped.
|
||||
func TestOnlyStaleTargetsAreDropped(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
|
|
@ -1655,12 +1655,14 @@ loop:
|
|||
if seriesAlreadyScraped && parsedTimestamp == nil {
|
||||
err = storage.ErrDuplicateSampleForTimestamp
|
||||
} else {
|
||||
if ctMs := p.CreatedTimestamp(); sl.enableCTZeroIngestion && ctMs != nil {
|
||||
ref, err = app.AppendCTZeroSample(ref, lset, t, *ctMs)
|
||||
if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { // OOO is a common case, ignoring completely for now.
|
||||
// CT is an experimental feature. For now, we don't need to fail the
|
||||
// scrape on errors updating the created timestamp, log debug.
|
||||
level.Debug(sl.l).Log("msg", "Error when appending CT in scrape loop", "series", string(met), "ct", *ctMs, "t", t, "err", err)
|
||||
if sl.enableCTZeroIngestion {
|
||||
if ctMs := p.CreatedTimestamp(); ctMs != nil {
|
||||
ref, err = app.AppendCTZeroSample(ref, lset, t, *ctMs)
|
||||
if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { // OOO is a common case, ignoring completely for now.
|
||||
// CT is an experimental feature. For now, we don't need to fail the
|
||||
// scrape on errors updating the created timestamp, log debug.
|
||||
level.Debug(sl.l).Log("msg", "Error when appending CT in scrape loop", "series", string(met), "ct", *ctMs, "t", t, "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
- name: Install Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
go-version: 1.23.x
|
||||
- name: Install snmp_exporter/generator dependencies
|
||||
run: sudo apt-get update && sudo apt-get -y install libsnmp-dev
|
||||
if: github.repository == 'prometheus/snmp_exporter'
|
||||
|
|
|
@ -37,7 +37,7 @@ if [ -z "${GITHUB_TOKEN}" ]; then
|
|||
fi
|
||||
|
||||
# List of files that should be synced.
|
||||
SYNC_FILES="CODE_OF_CONDUCT.md LICENSE Makefile.common SECURITY.md .yamllint scripts/golangci-lint.yml .github/workflows/scorecards.yml .github/workflows/container_description.yml"
|
||||
SYNC_FILES="CODE_OF_CONDUCT.md LICENSE Makefile.common SECURITY.md .yamllint scripts/golangci-lint.yml .github/workflows/scorecards.yml .github/workflows/container_description.yml .github/workflows/stale.yml"
|
||||
|
||||
# Go to the root of the repo
|
||||
cd "$(git rev-parse --show-cdup)" || exit 1
|
||||
|
|
37
storage/interface_test.go
Normal file
37
storage/interface_test.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2024 The Prometheus 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.
|
||||
|
||||
package storage_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/tsdb/chunkenc"
|
||||
)
|
||||
|
||||
func TestMockSeries(t *testing.T) {
|
||||
s := storage.MockSeries([]int64{1, 2, 3}, []float64{1, 2, 3}, []string{"__name__", "foo"})
|
||||
it := s.Iterator(nil)
|
||||
ts := []int64{}
|
||||
vs := []float64{}
|
||||
for it.Next() == chunkenc.ValFloat {
|
||||
t, v := it.At()
|
||||
ts = append(ts, t)
|
||||
vs = append(vs, v)
|
||||
}
|
||||
require.Equal(t, []int64{1, 2, 3}, ts)
|
||||
require.Equal(t, []float64{1, 2, 3}, vs)
|
||||
}
|
|
@ -24,7 +24,6 @@ import (
|
|||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
|
@ -594,5 +593,5 @@ func addResourceTargetInfo(resource pcommon.Resource, settings Settings, timesta
|
|||
|
||||
// convertTimeStamp converts OTLP timestamp in ns to timestamp in ms
|
||||
func convertTimeStamp(timestamp pcommon.Timestamp) int64 {
|
||||
return timestamp.AsTime().UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond))
|
||||
return int64(timestamp) / 1_000_000
|
||||
}
|
||||
|
|
|
@ -10,13 +10,21 @@
|
|||
// 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.
|
||||
// Provenance-includes-location: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/debbf30360b8d3a0ded8db09c4419d2a9c99b94a/pkg/translator/prometheusremotewrite/helper_test.go
|
||||
// Provenance-includes-license: Apache-2.0
|
||||
// Provenance-includes-copyright: Copyright The OpenTelemetry Authors.
|
||||
|
||||
package prometheusremotewrite
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.opentelemetry.io/collector/pdata/pcommon"
|
||||
"go.opentelemetry.io/collector/pdata/pmetric"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
)
|
||||
|
@ -159,3 +167,239 @@ func TestCreateAttributes(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_convertTimeStamp(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
arg pcommon.Timestamp
|
||||
want int64
|
||||
}{
|
||||
{"zero", 0, 0},
|
||||
{"1ms", 1_000_000, 1},
|
||||
{"1s", pcommon.Timestamp(time.Unix(1, 0).UnixNano()), 1000},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := convertTimeStamp(tt.arg)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusConverter_AddSummaryDataPoints(t *testing.T) {
|
||||
ts := pcommon.Timestamp(time.Now().UnixNano())
|
||||
tests := []struct {
|
||||
name string
|
||||
metric func() pmetric.Metric
|
||||
want func() map[uint64]*prompb.TimeSeries
|
||||
}{
|
||||
{
|
||||
name: "summary with start time",
|
||||
metric: func() pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName("test_summary")
|
||||
metric.SetEmptySummary()
|
||||
|
||||
dp := metric.Summary().DataPoints().AppendEmpty()
|
||||
dp.SetTimestamp(ts)
|
||||
dp.SetStartTimestamp(ts)
|
||||
|
||||
return metric
|
||||
},
|
||||
want: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_summary" + countStr},
|
||||
}
|
||||
createdLabels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_summary" + createdSuffix},
|
||||
}
|
||||
sumLabels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_summary" + sumStr},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 0, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
timeSeriesSignature(sumLabels): {
|
||||
Labels: sumLabels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 0, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
timeSeriesSignature(createdLabels): {
|
||||
Labels: createdLabels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: float64(convertTimeStamp(ts)), Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "summary without start time",
|
||||
metric: func() pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName("test_summary")
|
||||
metric.SetEmptySummary()
|
||||
|
||||
dp := metric.Summary().DataPoints().AppendEmpty()
|
||||
dp.SetTimestamp(ts)
|
||||
|
||||
return metric
|
||||
},
|
||||
want: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_summary" + countStr},
|
||||
}
|
||||
sumLabels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_summary" + sumStr},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 0, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
timeSeriesSignature(sumLabels): {
|
||||
Labels: sumLabels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 0, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
metric := tt.metric()
|
||||
converter := NewPrometheusConverter()
|
||||
|
||||
converter.addSummaryDataPoints(
|
||||
metric.Summary().DataPoints(),
|
||||
pcommon.NewResource(),
|
||||
Settings{
|
||||
ExportCreatedMetric: true,
|
||||
},
|
||||
metric.Name(),
|
||||
)
|
||||
|
||||
assert.Equal(t, tt.want(), converter.unique)
|
||||
assert.Empty(t, converter.conflicts)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusConverter_AddHistogramDataPoints(t *testing.T) {
|
||||
ts := pcommon.Timestamp(time.Now().UnixNano())
|
||||
tests := []struct {
|
||||
name string
|
||||
metric func() pmetric.Metric
|
||||
want func() map[uint64]*prompb.TimeSeries
|
||||
}{
|
||||
{
|
||||
name: "histogram with start time",
|
||||
metric: func() pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName("test_hist")
|
||||
metric.SetEmptyHistogram().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
||||
|
||||
pt := metric.Histogram().DataPoints().AppendEmpty()
|
||||
pt.SetTimestamp(ts)
|
||||
pt.SetStartTimestamp(ts)
|
||||
|
||||
return metric
|
||||
},
|
||||
want: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_hist" + countStr},
|
||||
}
|
||||
createdLabels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_hist" + createdSuffix},
|
||||
}
|
||||
infLabels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_hist_bucket"},
|
||||
{Name: model.BucketLabel, Value: "+Inf"},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(infLabels): {
|
||||
Labels: infLabels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 0, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 0, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
timeSeriesSignature(createdLabels): {
|
||||
Labels: createdLabels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: float64(convertTimeStamp(ts)), Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "histogram without start time",
|
||||
metric: func() pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName("test_hist")
|
||||
metric.SetEmptyHistogram().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
||||
|
||||
pt := metric.Histogram().DataPoints().AppendEmpty()
|
||||
pt.SetTimestamp(ts)
|
||||
|
||||
return metric
|
||||
},
|
||||
want: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_hist" + countStr},
|
||||
}
|
||||
infLabels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_hist_bucket"},
|
||||
{Name: model.BucketLabel, Value: "+Inf"},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(infLabels): {
|
||||
Labels: infLabels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 0, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 0, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
metric := tt.metric()
|
||||
converter := NewPrometheusConverter()
|
||||
|
||||
converter.addHistogramDataPoints(
|
||||
metric.Histogram().DataPoints(),
|
||||
pcommon.NewResource(),
|
||||
Settings{
|
||||
ExportCreatedMetric: true,
|
||||
},
|
||||
metric.Name(),
|
||||
)
|
||||
|
||||
assert.Equal(t, tt.want(), converter.unique)
|
||||
assert.Empty(t, converter.conflicts)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,771 @@
|
|||
// Copyright 2024 The Prometheus 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.
|
||||
// Provenance-includes-location: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/247a9f996e09a83cdc25addf70c05e42b8b30186/pkg/translator/prometheusremotewrite/histograms_test.go
|
||||
// Provenance-includes-license: Apache-2.0
|
||||
// Provenance-includes-copyright: Copyright The OpenTelemetry Authors.
|
||||
|
||||
package prometheusremotewrite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/collector/pdata/pcommon"
|
||||
"go.opentelemetry.io/collector/pdata/pmetric"
|
||||
|
||||
prometheustranslator "github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheus"
|
||||
)
|
||||
|
||||
type expectedBucketLayout struct {
|
||||
wantSpans []prompb.BucketSpan
|
||||
wantDeltas []int64
|
||||
}
|
||||
|
||||
func TestConvertBucketsLayout(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
buckets func() pmetric.ExponentialHistogramDataPointBuckets
|
||||
wantLayout map[int32]expectedBucketLayout
|
||||
}{
|
||||
{
|
||||
name: "zero offset",
|
||||
buckets: func() pmetric.ExponentialHistogramDataPointBuckets {
|
||||
b := pmetric.NewExponentialHistogramDataPointBuckets()
|
||||
b.SetOffset(0)
|
||||
b.BucketCounts().FromRaw([]uint64{4, 3, 2, 1})
|
||||
return b
|
||||
},
|
||||
wantLayout: map[int32]expectedBucketLayout{
|
||||
0: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 1,
|
||||
Length: 4,
|
||||
},
|
||||
},
|
||||
wantDeltas: []int64{4, -1, -1, -1},
|
||||
},
|
||||
1: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 1,
|
||||
Length: 2,
|
||||
},
|
||||
},
|
||||
// 4+3, 2+1 = 7, 3 =delta= 7, -4
|
||||
wantDeltas: []int64{7, -4},
|
||||
},
|
||||
2: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 1,
|
||||
Length: 1,
|
||||
},
|
||||
},
|
||||
// 4+3+2+1 = 10 =delta= 10
|
||||
wantDeltas: []int64{10},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "offset 1",
|
||||
buckets: func() pmetric.ExponentialHistogramDataPointBuckets {
|
||||
b := pmetric.NewExponentialHistogramDataPointBuckets()
|
||||
b.SetOffset(1)
|
||||
b.BucketCounts().FromRaw([]uint64{4, 3, 2, 1})
|
||||
return b
|
||||
},
|
||||
wantLayout: map[int32]expectedBucketLayout{
|
||||
0: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 2,
|
||||
Length: 4,
|
||||
},
|
||||
},
|
||||
wantDeltas: []int64{4, -1, -1, -1},
|
||||
},
|
||||
1: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 1,
|
||||
Length: 3,
|
||||
},
|
||||
},
|
||||
wantDeltas: []int64{4, 1, -4}, // 0+4, 3+2, 1+0 = 4, 5, 1
|
||||
},
|
||||
2: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 1,
|
||||
Length: 2,
|
||||
},
|
||||
},
|
||||
wantDeltas: []int64{9, -8}, // 0+4+3+2, 1+0+0+0 = 9, 1
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive offset",
|
||||
buckets: func() pmetric.ExponentialHistogramDataPointBuckets {
|
||||
b := pmetric.NewExponentialHistogramDataPointBuckets()
|
||||
b.SetOffset(4)
|
||||
b.BucketCounts().FromRaw([]uint64{4, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
|
||||
return b
|
||||
},
|
||||
wantLayout: map[int32]expectedBucketLayout{
|
||||
0: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 5,
|
||||
Length: 4,
|
||||
},
|
||||
{
|
||||
Offset: 12,
|
||||
Length: 1,
|
||||
},
|
||||
},
|
||||
wantDeltas: []int64{4, -2, -2, 2, -1},
|
||||
},
|
||||
1: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 3,
|
||||
Length: 2,
|
||||
},
|
||||
{
|
||||
Offset: 6,
|
||||
Length: 1,
|
||||
},
|
||||
},
|
||||
// Downscale:
|
||||
// 4+2, 0+2, 0+0, 0+0, 0+0, 0+0, 0+0, 0+0, 1+0 = 6, 2, 0, 0, 0, 0, 0, 0, 1
|
||||
wantDeltas: []int64{6, -4, -1},
|
||||
},
|
||||
2: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 2,
|
||||
Length: 1,
|
||||
},
|
||||
{
|
||||
Offset: 3,
|
||||
Length: 1,
|
||||
},
|
||||
},
|
||||
// Downscale:
|
||||
// 4+2+0+2, 0+0+0+0, 0+0+0+0, 0+0+0+0, 1+0+0+0 = 8, 0, 0, 0, 1
|
||||
// Check from sclaing from previous: 6+2, 0+0, 0+0, 0+0, 1+0 = 8, 0, 0, 0, 1
|
||||
wantDeltas: []int64{8, -7},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "scaledown merges spans",
|
||||
buckets: func() pmetric.ExponentialHistogramDataPointBuckets {
|
||||
b := pmetric.NewExponentialHistogramDataPointBuckets()
|
||||
b.SetOffset(4)
|
||||
b.BucketCounts().FromRaw([]uint64{4, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1})
|
||||
return b
|
||||
},
|
||||
wantLayout: map[int32]expectedBucketLayout{
|
||||
0: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 5,
|
||||
Length: 4,
|
||||
},
|
||||
{
|
||||
Offset: 8,
|
||||
Length: 1,
|
||||
},
|
||||
},
|
||||
wantDeltas: []int64{4, -2, -2, 2, -1},
|
||||
},
|
||||
1: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 3,
|
||||
Length: 2,
|
||||
},
|
||||
{
|
||||
Offset: 4,
|
||||
Length: 1,
|
||||
},
|
||||
},
|
||||
// Downscale:
|
||||
// 4+2, 0+2, 0+0, 0+0, 0+0, 0+0, 1+0 = 6, 2, 0, 0, 0, 0, 1
|
||||
wantDeltas: []int64{6, -4, -1},
|
||||
},
|
||||
2: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 2,
|
||||
Length: 4,
|
||||
},
|
||||
},
|
||||
// Downscale:
|
||||
// 4+2+0+2, 0+0+0+0, 0+0+0+0, 1+0+0+0 = 8, 0, 0, 1
|
||||
// Check from sclaing from previous: 6+2, 0+0, 0+0, 1+0 = 8, 0, 0, 1
|
||||
wantDeltas: []int64{8, -8, 0, 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "negative offset",
|
||||
buckets: func() pmetric.ExponentialHistogramDataPointBuckets {
|
||||
b := pmetric.NewExponentialHistogramDataPointBuckets()
|
||||
b.SetOffset(-2)
|
||||
b.BucketCounts().FromRaw([]uint64{3, 1, 0, 0, 0, 1})
|
||||
return b
|
||||
},
|
||||
wantLayout: map[int32]expectedBucketLayout{
|
||||
0: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: -1,
|
||||
Length: 2,
|
||||
},
|
||||
{
|
||||
Offset: 3,
|
||||
Length: 1,
|
||||
},
|
||||
},
|
||||
wantDeltas: []int64{3, -2, 0},
|
||||
},
|
||||
1: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 0,
|
||||
Length: 3,
|
||||
},
|
||||
},
|
||||
// Downscale:
|
||||
// 3+1, 0+0, 0+1 = 4, 0, 1
|
||||
wantDeltas: []int64{4, -4, 1},
|
||||
},
|
||||
2: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 0,
|
||||
Length: 2,
|
||||
},
|
||||
},
|
||||
// Downscale:
|
||||
// 0+0+3+1, 0+0+0+0 = 4, 1
|
||||
wantDeltas: []int64{4, -3},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "buckets with gaps of size 1",
|
||||
buckets: func() pmetric.ExponentialHistogramDataPointBuckets {
|
||||
b := pmetric.NewExponentialHistogramDataPointBuckets()
|
||||
b.SetOffset(-2)
|
||||
b.BucketCounts().FromRaw([]uint64{3, 1, 0, 1, 0, 1})
|
||||
return b
|
||||
},
|
||||
wantLayout: map[int32]expectedBucketLayout{
|
||||
0: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: -1,
|
||||
Length: 6,
|
||||
},
|
||||
},
|
||||
wantDeltas: []int64{3, -2, -1, 1, -1, 1},
|
||||
},
|
||||
1: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 0,
|
||||
Length: 3,
|
||||
},
|
||||
},
|
||||
// Downscale:
|
||||
// 3+1, 0+1, 0+1 = 4, 1, 1
|
||||
wantDeltas: []int64{4, -3, 0},
|
||||
},
|
||||
2: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 0,
|
||||
Length: 2,
|
||||
},
|
||||
},
|
||||
// Downscale:
|
||||
// 0+0+3+1, 0+1+0+1 = 4, 2
|
||||
wantDeltas: []int64{4, -2},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "buckets with gaps of size 2",
|
||||
buckets: func() pmetric.ExponentialHistogramDataPointBuckets {
|
||||
b := pmetric.NewExponentialHistogramDataPointBuckets()
|
||||
b.SetOffset(-2)
|
||||
b.BucketCounts().FromRaw([]uint64{3, 0, 0, 1, 0, 0, 1})
|
||||
return b
|
||||
},
|
||||
wantLayout: map[int32]expectedBucketLayout{
|
||||
0: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: -1,
|
||||
Length: 7,
|
||||
},
|
||||
},
|
||||
wantDeltas: []int64{3, -3, 0, 1, -1, 0, 1},
|
||||
},
|
||||
1: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 0,
|
||||
Length: 4,
|
||||
},
|
||||
},
|
||||
// Downscale:
|
||||
// 3+0, 0+1, 0+0, 0+1 = 3, 1, 0, 1
|
||||
wantDeltas: []int64{3, -2, -1, 1},
|
||||
},
|
||||
2: {
|
||||
wantSpans: []prompb.BucketSpan{
|
||||
{
|
||||
Offset: 0,
|
||||
Length: 3,
|
||||
},
|
||||
},
|
||||
// Downscale:
|
||||
// 0+0+3+0, 0+1+0+0, 1+0+0+0 = 3, 1, 1
|
||||
wantDeltas: []int64{3, -2, 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "zero buckets",
|
||||
buckets: pmetric.NewExponentialHistogramDataPointBuckets,
|
||||
wantLayout: map[int32]expectedBucketLayout{
|
||||
0: {
|
||||
wantSpans: nil,
|
||||
wantDeltas: nil,
|
||||
},
|
||||
1: {
|
||||
wantSpans: nil,
|
||||
wantDeltas: nil,
|
||||
},
|
||||
2: {
|
||||
wantSpans: nil,
|
||||
wantDeltas: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
for scaleDown, wantLayout := range tt.wantLayout {
|
||||
t.Run(fmt.Sprintf("%s-scaleby-%d", tt.name, scaleDown), func(t *testing.T) {
|
||||
gotSpans, gotDeltas := convertBucketsLayout(tt.buckets(), scaleDown)
|
||||
assert.Equal(t, wantLayout.wantSpans, gotSpans)
|
||||
assert.Equal(t, wantLayout.wantDeltas, gotDeltas)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConvertBucketLayout(b *testing.B) {
|
||||
scenarios := []struct {
|
||||
gap int
|
||||
}{
|
||||
{gap: 0},
|
||||
{gap: 1},
|
||||
{gap: 2},
|
||||
{gap: 3},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
buckets := pmetric.NewExponentialHistogramDataPointBuckets()
|
||||
buckets.SetOffset(0)
|
||||
for i := 0; i < 1000; i++ {
|
||||
if i%(scenario.gap+1) == 0 {
|
||||
buckets.BucketCounts().Append(10)
|
||||
} else {
|
||||
buckets.BucketCounts().Append(0)
|
||||
}
|
||||
}
|
||||
b.Run(fmt.Sprintf("gap %d", scenario.gap), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
convertBucketsLayout(buckets, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExponentialToNativeHistogram(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
exponentialHist func() pmetric.ExponentialHistogramDataPoint
|
||||
wantNativeHist func() prompb.Histogram
|
||||
wantErrMessage string
|
||||
}{
|
||||
{
|
||||
name: "convert exp. to native histogram",
|
||||
exponentialHist: func() pmetric.ExponentialHistogramDataPoint {
|
||||
pt := pmetric.NewExponentialHistogramDataPoint()
|
||||
pt.SetStartTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(100)))
|
||||
pt.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(500)))
|
||||
pt.SetCount(4)
|
||||
pt.SetSum(10.1)
|
||||
pt.SetScale(1)
|
||||
pt.SetZeroCount(1)
|
||||
|
||||
pt.Positive().BucketCounts().FromRaw([]uint64{1, 1})
|
||||
pt.Positive().SetOffset(1)
|
||||
|
||||
pt.Negative().BucketCounts().FromRaw([]uint64{1, 1})
|
||||
pt.Negative().SetOffset(1)
|
||||
|
||||
return pt
|
||||
},
|
||||
wantNativeHist: func() prompb.Histogram {
|
||||
return prompb.Histogram{
|
||||
Count: &prompb.Histogram_CountInt{CountInt: 4},
|
||||
Sum: 10.1,
|
||||
Schema: 1,
|
||||
ZeroThreshold: defaultZeroThreshold,
|
||||
ZeroCount: &prompb.Histogram_ZeroCountInt{ZeroCountInt: 1},
|
||||
NegativeSpans: []prompb.BucketSpan{{Offset: 2, Length: 2}},
|
||||
NegativeDeltas: []int64{1, 0},
|
||||
PositiveSpans: []prompb.BucketSpan{{Offset: 2, Length: 2}},
|
||||
PositiveDeltas: []int64{1, 0},
|
||||
Timestamp: 500,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "convert exp. to native histogram with no sum",
|
||||
exponentialHist: func() pmetric.ExponentialHistogramDataPoint {
|
||||
pt := pmetric.NewExponentialHistogramDataPoint()
|
||||
pt.SetStartTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(100)))
|
||||
pt.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(500)))
|
||||
|
||||
pt.SetCount(4)
|
||||
pt.SetScale(1)
|
||||
pt.SetZeroCount(1)
|
||||
|
||||
pt.Positive().BucketCounts().FromRaw([]uint64{1, 1})
|
||||
pt.Positive().SetOffset(1)
|
||||
|
||||
pt.Negative().BucketCounts().FromRaw([]uint64{1, 1})
|
||||
pt.Negative().SetOffset(1)
|
||||
|
||||
return pt
|
||||
},
|
||||
wantNativeHist: func() prompb.Histogram {
|
||||
return prompb.Histogram{
|
||||
Count: &prompb.Histogram_CountInt{CountInt: 4},
|
||||
Schema: 1,
|
||||
ZeroThreshold: defaultZeroThreshold,
|
||||
ZeroCount: &prompb.Histogram_ZeroCountInt{ZeroCountInt: 1},
|
||||
NegativeSpans: []prompb.BucketSpan{{Offset: 2, Length: 2}},
|
||||
NegativeDeltas: []int64{1, 0},
|
||||
PositiveSpans: []prompb.BucketSpan{{Offset: 2, Length: 2}},
|
||||
PositiveDeltas: []int64{1, 0},
|
||||
Timestamp: 500,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid negative scale",
|
||||
exponentialHist: func() pmetric.ExponentialHistogramDataPoint {
|
||||
pt := pmetric.NewExponentialHistogramDataPoint()
|
||||
pt.SetScale(-10)
|
||||
return pt
|
||||
},
|
||||
wantErrMessage: "cannot convert exponential to native histogram." +
|
||||
" Scale must be >= -4, was -10",
|
||||
},
|
||||
{
|
||||
name: "no downscaling at scale 8",
|
||||
exponentialHist: func() pmetric.ExponentialHistogramDataPoint {
|
||||
pt := pmetric.NewExponentialHistogramDataPoint()
|
||||
pt.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(500)))
|
||||
pt.SetCount(6)
|
||||
pt.SetSum(10.1)
|
||||
pt.SetScale(8)
|
||||
pt.SetZeroCount(1)
|
||||
|
||||
pt.Positive().BucketCounts().FromRaw([]uint64{1, 1, 1})
|
||||
pt.Positive().SetOffset(1)
|
||||
|
||||
pt.Negative().BucketCounts().FromRaw([]uint64{1, 1, 1})
|
||||
pt.Negative().SetOffset(2)
|
||||
return pt
|
||||
},
|
||||
wantNativeHist: func() prompb.Histogram {
|
||||
return prompb.Histogram{
|
||||
Count: &prompb.Histogram_CountInt{CountInt: 6},
|
||||
Sum: 10.1,
|
||||
Schema: 8,
|
||||
ZeroThreshold: defaultZeroThreshold,
|
||||
ZeroCount: &prompb.Histogram_ZeroCountInt{ZeroCountInt: 1},
|
||||
PositiveSpans: []prompb.BucketSpan{{Offset: 2, Length: 3}},
|
||||
PositiveDeltas: []int64{1, 0, 0}, // 1, 1, 1
|
||||
NegativeSpans: []prompb.BucketSpan{{Offset: 3, Length: 3}},
|
||||
NegativeDeltas: []int64{1, 0, 0}, // 1, 1, 1
|
||||
Timestamp: 500,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "downsample if scale is more than 8",
|
||||
exponentialHist: func() pmetric.ExponentialHistogramDataPoint {
|
||||
pt := pmetric.NewExponentialHistogramDataPoint()
|
||||
pt.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(500)))
|
||||
pt.SetCount(6)
|
||||
pt.SetSum(10.1)
|
||||
pt.SetScale(9)
|
||||
pt.SetZeroCount(1)
|
||||
|
||||
pt.Positive().BucketCounts().FromRaw([]uint64{1, 1, 1})
|
||||
pt.Positive().SetOffset(1)
|
||||
|
||||
pt.Negative().BucketCounts().FromRaw([]uint64{1, 1, 1})
|
||||
pt.Negative().SetOffset(2)
|
||||
return pt
|
||||
},
|
||||
wantNativeHist: func() prompb.Histogram {
|
||||
return prompb.Histogram{
|
||||
Count: &prompb.Histogram_CountInt{CountInt: 6},
|
||||
Sum: 10.1,
|
||||
Schema: 8,
|
||||
ZeroThreshold: defaultZeroThreshold,
|
||||
ZeroCount: &prompb.Histogram_ZeroCountInt{ZeroCountInt: 1},
|
||||
PositiveSpans: []prompb.BucketSpan{{Offset: 1, Length: 2}},
|
||||
PositiveDeltas: []int64{1, 1}, // 0+1, 1+1 = 1, 2
|
||||
NegativeSpans: []prompb.BucketSpan{{Offset: 2, Length: 2}},
|
||||
NegativeDeltas: []int64{2, -1}, // 1+1, 1+0 = 2, 1
|
||||
Timestamp: 500,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
validateExponentialHistogramCount(t, tt.exponentialHist()) // Sanity check.
|
||||
got, annots, err := exponentialToNativeHistogram(tt.exponentialHist())
|
||||
if tt.wantErrMessage != "" {
|
||||
assert.ErrorContains(t, err, tt.wantErrMessage)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, annots)
|
||||
assert.Equal(t, tt.wantNativeHist(), got)
|
||||
validateNativeHistogramCount(t, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func validateExponentialHistogramCount(t *testing.T, h pmetric.ExponentialHistogramDataPoint) {
|
||||
actualCount := uint64(0)
|
||||
for _, bucket := range h.Positive().BucketCounts().AsRaw() {
|
||||
actualCount += bucket
|
||||
}
|
||||
for _, bucket := range h.Negative().BucketCounts().AsRaw() {
|
||||
actualCount += bucket
|
||||
}
|
||||
require.Equal(t, h.Count(), actualCount, "exponential histogram count mismatch")
|
||||
}
|
||||
|
||||
func validateNativeHistogramCount(t *testing.T, h prompb.Histogram) {
|
||||
require.NotNil(t, h.Count)
|
||||
require.IsType(t, &prompb.Histogram_CountInt{}, h.Count)
|
||||
want := h.Count.(*prompb.Histogram_CountInt).CountInt
|
||||
var (
|
||||
actualCount uint64
|
||||
prevBucket int64
|
||||
)
|
||||
for _, delta := range h.PositiveDeltas {
|
||||
prevBucket += delta
|
||||
actualCount += uint64(prevBucket)
|
||||
}
|
||||
prevBucket = 0
|
||||
for _, delta := range h.NegativeDeltas {
|
||||
prevBucket += delta
|
||||
actualCount += uint64(prevBucket)
|
||||
}
|
||||
assert.Equal(t, want, actualCount, "native histogram count mismatch")
|
||||
}
|
||||
|
||||
func TestPrometheusConverter_addExponentialHistogramDataPoints(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
metric func() pmetric.Metric
|
||||
wantSeries func() map[uint64]*prompb.TimeSeries
|
||||
}{
|
||||
{
|
||||
name: "histogram data points with same labels",
|
||||
metric: func() pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName("test_hist")
|
||||
metric.SetEmptyExponentialHistogram().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
||||
|
||||
pt := metric.ExponentialHistogram().DataPoints().AppendEmpty()
|
||||
pt.SetCount(7)
|
||||
pt.SetScale(1)
|
||||
pt.Positive().SetOffset(-1)
|
||||
pt.Positive().BucketCounts().FromRaw([]uint64{4, 2})
|
||||
pt.Exemplars().AppendEmpty().SetDoubleValue(1)
|
||||
pt.Attributes().PutStr("attr", "test_attr")
|
||||
|
||||
pt = metric.ExponentialHistogram().DataPoints().AppendEmpty()
|
||||
pt.SetCount(4)
|
||||
pt.SetScale(1)
|
||||
pt.Positive().SetOffset(-1)
|
||||
pt.Positive().BucketCounts().FromRaw([]uint64{4, 2, 1})
|
||||
pt.Exemplars().AppendEmpty().SetDoubleValue(2)
|
||||
pt.Attributes().PutStr("attr", "test_attr")
|
||||
|
||||
return metric
|
||||
},
|
||||
wantSeries: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_hist"},
|
||||
{Name: "attr", Value: "test_attr"},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Histograms: []prompb.Histogram{
|
||||
{
|
||||
Count: &prompb.Histogram_CountInt{CountInt: 7},
|
||||
Schema: 1,
|
||||
ZeroThreshold: defaultZeroThreshold,
|
||||
ZeroCount: &prompb.Histogram_ZeroCountInt{ZeroCountInt: 0},
|
||||
PositiveSpans: []prompb.BucketSpan{{Offset: 0, Length: 2}},
|
||||
PositiveDeltas: []int64{4, -2},
|
||||
},
|
||||
{
|
||||
Count: &prompb.Histogram_CountInt{CountInt: 4},
|
||||
Schema: 1,
|
||||
ZeroThreshold: defaultZeroThreshold,
|
||||
ZeroCount: &prompb.Histogram_ZeroCountInt{ZeroCountInt: 0},
|
||||
PositiveSpans: []prompb.BucketSpan{{Offset: 0, Length: 3}},
|
||||
PositiveDeltas: []int64{4, -2, -1},
|
||||
},
|
||||
},
|
||||
Exemplars: []prompb.Exemplar{
|
||||
{Value: 1},
|
||||
{Value: 2},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "histogram data points with different labels",
|
||||
metric: func() pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName("test_hist")
|
||||
metric.SetEmptyExponentialHistogram().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
||||
|
||||
pt := metric.ExponentialHistogram().DataPoints().AppendEmpty()
|
||||
pt.SetCount(7)
|
||||
pt.SetScale(1)
|
||||
pt.Positive().SetOffset(-1)
|
||||
pt.Positive().BucketCounts().FromRaw([]uint64{4, 2})
|
||||
pt.Exemplars().AppendEmpty().SetDoubleValue(1)
|
||||
pt.Attributes().PutStr("attr", "test_attr")
|
||||
|
||||
pt = metric.ExponentialHistogram().DataPoints().AppendEmpty()
|
||||
pt.SetCount(4)
|
||||
pt.SetScale(1)
|
||||
pt.Negative().SetOffset(-1)
|
||||
pt.Negative().BucketCounts().FromRaw([]uint64{4, 2, 1})
|
||||
pt.Exemplars().AppendEmpty().SetDoubleValue(2)
|
||||
pt.Attributes().PutStr("attr", "test_attr_two")
|
||||
|
||||
return metric
|
||||
},
|
||||
wantSeries: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_hist"},
|
||||
{Name: "attr", Value: "test_attr"},
|
||||
}
|
||||
labelsAnother := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_hist"},
|
||||
{Name: "attr", Value: "test_attr_two"},
|
||||
}
|
||||
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Histograms: []prompb.Histogram{
|
||||
{
|
||||
Count: &prompb.Histogram_CountInt{CountInt: 7},
|
||||
Schema: 1,
|
||||
ZeroThreshold: defaultZeroThreshold,
|
||||
ZeroCount: &prompb.Histogram_ZeroCountInt{ZeroCountInt: 0},
|
||||
PositiveSpans: []prompb.BucketSpan{{Offset: 0, Length: 2}},
|
||||
PositiveDeltas: []int64{4, -2},
|
||||
},
|
||||
},
|
||||
Exemplars: []prompb.Exemplar{
|
||||
{Value: 1},
|
||||
},
|
||||
},
|
||||
timeSeriesSignature(labelsAnother): {
|
||||
Labels: labelsAnother,
|
||||
Histograms: []prompb.Histogram{
|
||||
{
|
||||
Count: &prompb.Histogram_CountInt{CountInt: 4},
|
||||
Schema: 1,
|
||||
ZeroThreshold: defaultZeroThreshold,
|
||||
ZeroCount: &prompb.Histogram_ZeroCountInt{ZeroCountInt: 0},
|
||||
NegativeSpans: []prompb.BucketSpan{{Offset: 0, Length: 3}},
|
||||
NegativeDeltas: []int64{4, -2, -1},
|
||||
},
|
||||
},
|
||||
Exemplars: []prompb.Exemplar{
|
||||
{Value: 2},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
metric := tt.metric()
|
||||
|
||||
converter := NewPrometheusConverter()
|
||||
annots, err := converter.addExponentialHistogramDataPoints(
|
||||
metric.ExponentialHistogram().DataPoints(),
|
||||
pcommon.NewResource(),
|
||||
Settings{
|
||||
ExportCreatedMetric: true,
|
||||
},
|
||||
prometheustranslator.BuildCompliantName(metric, "", true),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, annots)
|
||||
|
||||
assert.Equal(t, tt.wantSeries(), converter.unique)
|
||||
assert.Empty(t, converter.conflicts)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
// Copyright 2024 The Prometheus 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.
|
||||
// Provenance-includes-location: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/247a9f996e09a83cdc25addf70c05e42b8b30186/pkg/translator/prometheusremotewrite/number_data_points_test.go
|
||||
// Provenance-includes-license: Apache-2.0
|
||||
// Provenance-includes-copyright: Copyright The OpenTelemetry Authors.
|
||||
|
||||
package prometheusremotewrite
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.opentelemetry.io/collector/pdata/pcommon"
|
||||
"go.opentelemetry.io/collector/pdata/pmetric"
|
||||
)
|
||||
|
||||
func TestPrometheusConverter_addGaugeNumberDataPoints(t *testing.T) {
|
||||
ts := uint64(time.Now().UnixNano())
|
||||
tests := []struct {
|
||||
name string
|
||||
metric func() pmetric.Metric
|
||||
want func() map[uint64]*prompb.TimeSeries
|
||||
}{
|
||||
{
|
||||
name: "gauge",
|
||||
metric: func() pmetric.Metric {
|
||||
return getIntGaugeMetric(
|
||||
"test",
|
||||
pcommon.NewMap(),
|
||||
1, ts,
|
||||
)
|
||||
},
|
||||
want: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test"},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{
|
||||
{
|
||||
Value: 1,
|
||||
Timestamp: convertTimeStamp(pcommon.Timestamp(ts)),
|
||||
}},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
metric := tt.metric()
|
||||
converter := NewPrometheusConverter()
|
||||
|
||||
converter.addGaugeNumberDataPoints(
|
||||
metric.Gauge().DataPoints(),
|
||||
pcommon.NewResource(),
|
||||
Settings{
|
||||
ExportCreatedMetric: true,
|
||||
},
|
||||
metric.Name(),
|
||||
)
|
||||
|
||||
assert.Equal(t, tt.want(), converter.unique)
|
||||
assert.Empty(t, converter.conflicts)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusConverter_addSumNumberDataPoints(t *testing.T) {
|
||||
ts := pcommon.Timestamp(time.Now().UnixNano())
|
||||
tests := []struct {
|
||||
name string
|
||||
metric func() pmetric.Metric
|
||||
want func() map[uint64]*prompb.TimeSeries
|
||||
}{
|
||||
{
|
||||
name: "sum",
|
||||
metric: func() pmetric.Metric {
|
||||
return getIntSumMetric(
|
||||
"test",
|
||||
pcommon.NewMap(),
|
||||
1,
|
||||
uint64(ts.AsTime().UnixNano()),
|
||||
)
|
||||
},
|
||||
want: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test"},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{
|
||||
{
|
||||
Value: 1,
|
||||
Timestamp: convertTimeStamp(ts),
|
||||
}},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sum with exemplars",
|
||||
metric: func() pmetric.Metric {
|
||||
m := getIntSumMetric(
|
||||
"test",
|
||||
pcommon.NewMap(),
|
||||
1,
|
||||
uint64(ts.AsTime().UnixNano()),
|
||||
)
|
||||
m.Sum().DataPoints().At(0).Exemplars().AppendEmpty().SetDoubleValue(2)
|
||||
return m
|
||||
},
|
||||
want: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test"},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{{
|
||||
Value: 1,
|
||||
Timestamp: convertTimeStamp(ts),
|
||||
}},
|
||||
Exemplars: []prompb.Exemplar{
|
||||
{Value: 2},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "monotonic cumulative sum with start timestamp",
|
||||
metric: func() pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName("test_sum")
|
||||
metric.SetEmptySum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
||||
metric.SetEmptySum().SetIsMonotonic(true)
|
||||
|
||||
dp := metric.Sum().DataPoints().AppendEmpty()
|
||||
dp.SetDoubleValue(1)
|
||||
dp.SetTimestamp(ts)
|
||||
dp.SetStartTimestamp(ts)
|
||||
|
||||
return metric
|
||||
},
|
||||
want: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_sum"},
|
||||
}
|
||||
createdLabels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_sum" + createdSuffix},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 1, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
timeSeriesSignature(createdLabels): {
|
||||
Labels: createdLabels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: float64(convertTimeStamp(ts)), Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "monotonic cumulative sum with no start time",
|
||||
metric: func() pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName("test_sum")
|
||||
metric.SetEmptySum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
||||
metric.SetEmptySum().SetIsMonotonic(true)
|
||||
|
||||
dp := metric.Sum().DataPoints().AppendEmpty()
|
||||
dp.SetTimestamp(ts)
|
||||
|
||||
return metric
|
||||
},
|
||||
want: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_sum"},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 0, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non-monotonic cumulative sum with start time",
|
||||
metric: func() pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName("test_sum")
|
||||
metric.SetEmptySum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
||||
metric.SetEmptySum().SetIsMonotonic(false)
|
||||
|
||||
dp := metric.Sum().DataPoints().AppendEmpty()
|
||||
dp.SetTimestamp(ts)
|
||||
|
||||
return metric
|
||||
},
|
||||
want: func() map[uint64]*prompb.TimeSeries {
|
||||
labels := []prompb.Label{
|
||||
{Name: model.MetricNameLabel, Value: "test_sum"},
|
||||
}
|
||||
return map[uint64]*prompb.TimeSeries{
|
||||
timeSeriesSignature(labels): {
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{
|
||||
{Value: 0, Timestamp: convertTimeStamp(ts)},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
metric := tt.metric()
|
||||
converter := NewPrometheusConverter()
|
||||
|
||||
converter.addSumNumberDataPoints(
|
||||
metric.Sum().DataPoints(),
|
||||
pcommon.NewResource(),
|
||||
metric,
|
||||
Settings{
|
||||
ExportCreatedMetric: true,
|
||||
},
|
||||
metric.Name(),
|
||||
)
|
||||
|
||||
assert.Equal(t, tt.want(), converter.unique)
|
||||
assert.Empty(t, converter.conflicts)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2024 The Prometheus 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.
|
||||
// Provenance-includes-location: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/247a9f996e09a83cdc25addf70c05e42b8b30186/pkg/translator/prometheusremotewrite/testutil_test.go
|
||||
// Provenance-includes-license: Apache-2.0
|
||||
// Provenance-includes-copyright: Copyright The OpenTelemetry Authors.
|
||||
|
||||
package prometheusremotewrite
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/collector/pdata/pcommon"
|
||||
"go.opentelemetry.io/collector/pdata/pmetric"
|
||||
)
|
||||
|
||||
func getIntGaugeMetric(name string, attributes pcommon.Map, value int64, ts uint64) pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName(name)
|
||||
dp := metric.SetEmptyGauge().DataPoints().AppendEmpty()
|
||||
if strings.HasPrefix(name, "staleNaN") {
|
||||
dp.SetFlags(pmetric.DefaultDataPointFlags.WithNoRecordedValue(true))
|
||||
}
|
||||
dp.SetIntValue(value)
|
||||
attributes.CopyTo(dp.Attributes())
|
||||
|
||||
dp.SetStartTimestamp(pcommon.Timestamp(0))
|
||||
dp.SetTimestamp(pcommon.Timestamp(ts))
|
||||
return metric
|
||||
}
|
||||
|
||||
func getIntSumMetric(name string, attributes pcommon.Map, value int64, ts uint64) pmetric.Metric {
|
||||
metric := pmetric.NewMetric()
|
||||
metric.SetName(name)
|
||||
metric.SetEmptySum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
||||
dp := metric.Sum().DataPoints().AppendEmpty()
|
||||
if strings.HasPrefix(name, "staleNaN") {
|
||||
dp.SetFlags(pmetric.DefaultDataPointFlags.WithNoRecordedValue(true))
|
||||
}
|
||||
dp.SetIntValue(value)
|
||||
attributes.CopyTo(dp.Attributes())
|
||||
|
||||
dp.SetStartTimestamp(pcommon.Timestamp(0))
|
||||
dp.SetTimestamp(pcommon.Timestamp(ts))
|
||||
return metric
|
||||
}
|
|
@ -1118,7 +1118,7 @@ func (a *appender) logSeries() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// mintTs returns the minimum timestamp that a sample can have
|
||||
// minValidTime returns the minimum timestamp that a sample can have
|
||||
// and is needed for preventing underflow.
|
||||
func (a *appender) minValidTime(lastTs int64) int64 {
|
||||
if lastTs < math.MinInt64+a.opts.OutOfOrderTimeWindow {
|
||||
|
|
|
@ -213,7 +213,7 @@ func MockSeriesIterator(timestamps []int64, values []float64) Iterator {
|
|||
return &mockSeriesIterator{
|
||||
timeStamps: timestamps,
|
||||
values: values,
|
||||
currIndex: 0,
|
||||
currIndex: -1,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -974,6 +974,7 @@ func (it *floatHistogramIterator) Reset(b []byte) {
|
|||
if it.atFloatHistogramCalled {
|
||||
it.atFloatHistogramCalled = false
|
||||
it.pBuckets, it.nBuckets = nil, nil
|
||||
it.pSpans, it.nSpans = nil, nil
|
||||
} else {
|
||||
it.pBuckets, it.nBuckets = it.pBuckets[:0], it.nBuckets[:0]
|
||||
}
|
||||
|
@ -1069,7 +1070,7 @@ func (it *floatHistogramIterator) Next() ValueType {
|
|||
// The case for the 2nd sample with single deltas is implicitly handled correctly with the double delta code,
|
||||
// so we don't need a separate single delta logic for the 2nd sample.
|
||||
|
||||
// Recycle bucket slices that have not been returned yet. Otherwise, copy them.
|
||||
// Recycle bucket and span slices that have not been returned yet. Otherwise, copy them.
|
||||
// We can always recycle the slices for leading and trailing bits as they are
|
||||
// never returned to the caller.
|
||||
if it.atFloatHistogramCalled {
|
||||
|
@ -1088,6 +1089,20 @@ func (it *floatHistogramIterator) Next() ValueType {
|
|||
} else {
|
||||
it.nBuckets = nil
|
||||
}
|
||||
if len(it.pSpans) > 0 {
|
||||
newSpans := make([]histogram.Span, len(it.pSpans))
|
||||
copy(newSpans, it.pSpans)
|
||||
it.pSpans = newSpans
|
||||
} else {
|
||||
it.pSpans = nil
|
||||
}
|
||||
if len(it.nSpans) > 0 {
|
||||
newSpans := make([]histogram.Span, len(it.nSpans))
|
||||
copy(newSpans, it.nSpans)
|
||||
it.nSpans = newSpans
|
||||
} else {
|
||||
it.nSpans = nil
|
||||
}
|
||||
}
|
||||
|
||||
tDod, err := readVarbitInt(&it.br)
|
||||
|
|
|
@ -1306,3 +1306,54 @@ func TestFloatHistogramAppendOnlyErrors(t *testing.T) {
|
|||
require.EqualError(t, err, "float histogram counter reset")
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloatHistogramUniqueSpansAfterNext(t *testing.T) {
|
||||
// Create two histograms with the same schema and spans.
|
||||
h1 := &histogram.FloatHistogram{
|
||||
Schema: 1,
|
||||
ZeroThreshold: 1e-100,
|
||||
Count: 10,
|
||||
ZeroCount: 2,
|
||||
Sum: 15.0,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 2},
|
||||
{Offset: 1, Length: 2},
|
||||
},
|
||||
PositiveBuckets: []float64{1, 2, 3, 4},
|
||||
NegativeSpans: []histogram.Span{
|
||||
{Offset: 1, Length: 1},
|
||||
},
|
||||
NegativeBuckets: []float64{2},
|
||||
}
|
||||
|
||||
h2 := h1.Copy()
|
||||
|
||||
// Create a chunk and append both histograms.
|
||||
c := NewFloatHistogramChunk()
|
||||
app, err := c.Appender()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendFloatHistogram(nil, 0, h1, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendFloatHistogram(nil, 1, h2, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create an iterator and advance to the first histogram.
|
||||
it := c.Iterator(nil)
|
||||
require.Equal(t, ValFloatHistogram, it.Next())
|
||||
_, rh1 := it.AtFloatHistogram(nil)
|
||||
|
||||
// Advance to the second histogram and retrieve it.
|
||||
require.Equal(t, ValFloatHistogram, it.Next())
|
||||
_, rh2 := it.AtFloatHistogram(nil)
|
||||
|
||||
require.Equal(t, rh1.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh1.NegativeSpans, h1.NegativeSpans, "Returned negative spans are as expected")
|
||||
require.Equal(t, rh2.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh2.NegativeSpans, h1.NegativeSpans, "Returned negative spans are as expected")
|
||||
|
||||
// Check that the spans for h1 and h2 are unique slices.
|
||||
require.NotSame(t, &rh1.PositiveSpans[0], &rh2.PositiveSpans[0], "PositiveSpans should be unique between histograms")
|
||||
require.NotSame(t, &rh1.NegativeSpans[0], &rh2.NegativeSpans[0], "NegativeSpans should be unique between histograms")
|
||||
}
|
||||
|
|
|
@ -1073,6 +1073,7 @@ func (it *histogramIterator) Reset(b []byte) {
|
|||
if it.atHistogramCalled {
|
||||
it.atHistogramCalled = false
|
||||
it.pBuckets, it.nBuckets = nil, nil
|
||||
it.pSpans, it.nSpans = nil, nil
|
||||
} else {
|
||||
it.pBuckets = it.pBuckets[:0]
|
||||
it.nBuckets = it.nBuckets[:0]
|
||||
|
@ -1185,8 +1186,25 @@ func (it *histogramIterator) Next() ValueType {
|
|||
// The case for the 2nd sample with single deltas is implicitly handled correctly with the double delta code,
|
||||
// so we don't need a separate single delta logic for the 2nd sample.
|
||||
|
||||
// Recycle bucket slices that have not been returned yet. Otherwise,
|
||||
// Recycle bucket and span slices that have not been returned yet. Otherwise, copy them.
|
||||
// copy them.
|
||||
if it.atFloatHistogramCalled || it.atHistogramCalled {
|
||||
if len(it.pSpans) > 0 {
|
||||
newSpans := make([]histogram.Span, len(it.pSpans))
|
||||
copy(newSpans, it.pSpans)
|
||||
it.pSpans = newSpans
|
||||
} else {
|
||||
it.pSpans = nil
|
||||
}
|
||||
if len(it.nSpans) > 0 {
|
||||
newSpans := make([]histogram.Span, len(it.nSpans))
|
||||
copy(newSpans, it.nSpans)
|
||||
it.nSpans = newSpans
|
||||
} else {
|
||||
it.nSpans = nil
|
||||
}
|
||||
}
|
||||
|
||||
if it.atHistogramCalled {
|
||||
it.atHistogramCalled = false
|
||||
if len(it.pBuckets) > 0 {
|
||||
|
@ -1204,6 +1222,7 @@ func (it *histogramIterator) Next() ValueType {
|
|||
it.nBuckets = nil
|
||||
}
|
||||
}
|
||||
|
||||
// FloatBuckets are set from scratch, so simply create empty ones.
|
||||
if it.atFloatHistogramCalled {
|
||||
it.atFloatHistogramCalled = false
|
||||
|
|
|
@ -1487,6 +1487,108 @@ func TestHistogramAppendOnlyErrors(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestHistogramUniqueSpansAfterNext(t *testing.T) {
|
||||
// Create two histograms with the same schema and spans.
|
||||
h1 := &histogram.Histogram{
|
||||
Schema: 1,
|
||||
ZeroThreshold: 1e-100,
|
||||
Count: 10,
|
||||
ZeroCount: 2,
|
||||
Sum: 15.0,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 2},
|
||||
{Offset: 1, Length: 2},
|
||||
},
|
||||
PositiveBuckets: []int64{1, 2, 3, 4},
|
||||
NegativeSpans: []histogram.Span{
|
||||
{Offset: 1, Length: 1},
|
||||
},
|
||||
NegativeBuckets: []int64{2},
|
||||
}
|
||||
|
||||
h2 := h1.Copy()
|
||||
|
||||
// Create a chunk and append both histograms.
|
||||
c := NewHistogramChunk()
|
||||
app, err := c.Appender()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendHistogram(nil, 0, h1, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendHistogram(nil, 1, h2, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create an iterator and advance to the first histogram.
|
||||
it := c.Iterator(nil)
|
||||
require.Equal(t, ValHistogram, it.Next())
|
||||
_, rh1 := it.AtHistogram(nil)
|
||||
|
||||
// Advance to the second histogram and retrieve it.
|
||||
require.Equal(t, ValHistogram, it.Next())
|
||||
_, rh2 := it.AtHistogram(nil)
|
||||
|
||||
require.Equal(t, rh1.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh1.NegativeSpans, h1.NegativeSpans, "Returned negative spans are as expected")
|
||||
require.Equal(t, rh2.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh2.NegativeSpans, h1.NegativeSpans, "Returned negative spans are as expected")
|
||||
|
||||
// Check that the spans for h1 and h2 are unique slices.
|
||||
require.NotSame(t, &rh1.PositiveSpans[0], &rh2.PositiveSpans[0], "PositiveSpans should be unique between histograms")
|
||||
require.NotSame(t, &rh1.NegativeSpans[0], &rh2.NegativeSpans[0], "NegativeSpans should be unique between histograms")
|
||||
}
|
||||
|
||||
func TestHistogramUniqueSpansAfterNextWithAtFloatHistogram(t *testing.T) {
|
||||
// Create two histograms with the same schema and spans.
|
||||
h1 := &histogram.Histogram{
|
||||
Schema: 1,
|
||||
ZeroThreshold: 1e-100,
|
||||
Count: 10,
|
||||
ZeroCount: 2,
|
||||
Sum: 15.0,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 2},
|
||||
{Offset: 1, Length: 2},
|
||||
},
|
||||
PositiveBuckets: []int64{1, 2, 3, 4},
|
||||
NegativeSpans: []histogram.Span{
|
||||
{Offset: 1, Length: 1},
|
||||
},
|
||||
NegativeBuckets: []int64{2},
|
||||
}
|
||||
|
||||
h2 := h1.Copy()
|
||||
|
||||
// Create a chunk and append both histograms.
|
||||
c := NewHistogramChunk()
|
||||
app, err := c.Appender()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendHistogram(nil, 0, h1, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendHistogram(nil, 1, h2, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create an iterator and advance to the first histogram.
|
||||
it := c.Iterator(nil)
|
||||
require.Equal(t, ValHistogram, it.Next())
|
||||
_, rh1 := it.AtFloatHistogram(nil)
|
||||
|
||||
// Advance to the second histogram and retrieve it.
|
||||
require.Equal(t, ValHistogram, it.Next())
|
||||
_, rh2 := it.AtFloatHistogram(nil)
|
||||
|
||||
require.Equal(t, rh1.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh1.NegativeSpans, h1.NegativeSpans, "Returned negative spans are as expected")
|
||||
require.Equal(t, rh2.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh2.NegativeSpans, h1.NegativeSpans, "Returned negative spans are as expected")
|
||||
|
||||
// Check that the spans for h1 and h2 are unique slices.
|
||||
require.NotSame(t, &rh1.PositiveSpans[0], &rh2.PositiveSpans[0], "PositiveSpans should be unique between histograms")
|
||||
require.NotSame(t, &rh1.NegativeSpans[0], &rh2.NegativeSpans[0], "NegativeSpans should be unique between histograms")
|
||||
}
|
||||
|
||||
func BenchmarkAppendable(b *testing.B) {
|
||||
// Create a histogram with a bunch of spans and buckets.
|
||||
const (
|
||||
|
|
|
@ -408,7 +408,7 @@ func TestChunkDiskMapper_Truncate_WriteQueueRaceCondition(t *testing.T) {
|
|||
wg.Wait()
|
||||
}
|
||||
|
||||
// TestHeadReadWriter_TruncateAfterIterateChunksError tests for
|
||||
// TestHeadReadWriter_TruncateAfterFailedIterateChunks tests for
|
||||
// https://github.com/prometheus/prometheus/issues/7753
|
||||
func TestHeadReadWriter_TruncateAfterFailedIterateChunks(t *testing.T) {
|
||||
hrw := createChunkDiskMapper(t, "")
|
||||
|
|
|
@ -128,6 +128,7 @@ type Head struct {
|
|||
writeNotified wlog.WriteNotified
|
||||
|
||||
memTruncationInProcess atomic.Bool
|
||||
memTruncationCallBack func() // For testing purposes.
|
||||
}
|
||||
|
||||
type ExemplarStorage interface {
|
||||
|
@ -1144,6 +1145,10 @@ func (h *Head) truncateMemory(mint int64) (err error) {
|
|||
h.memTruncationInProcess.Store(true)
|
||||
defer h.memTruncationInProcess.Store(false)
|
||||
|
||||
if h.memTruncationCallBack != nil {
|
||||
h.memTruncationCallBack()
|
||||
}
|
||||
|
||||
// We wait for pending queries to end that overlap with this truncation.
|
||||
if initialized {
|
||||
h.WaitForPendingReadersInTimeRange(h.MinTime(), mint)
|
||||
|
|
|
@ -33,7 +33,6 @@ import (
|
|||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
prom_testutil "github.com/prometheus/client_golang/prometheus/testutil"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/atomic"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
@ -438,6 +437,32 @@ func BenchmarkLoadWLs(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
// BenchmarkLoadRealWLs will be skipped unless the BENCHMARK_LOAD_REAL_WLS_DIR environment variable is set.
|
||||
// BENCHMARK_LOAD_REAL_WLS_DIR should be the folder where `wal` and `chunks_head` are located.
|
||||
func BenchmarkLoadRealWLs(b *testing.B) {
|
||||
dir := os.Getenv("BENCHMARK_LOAD_REAL_WLS_DIR")
|
||||
if dir == "" {
|
||||
b.SkipNow()
|
||||
}
|
||||
|
||||
wal, err := wlog.New(nil, nil, filepath.Join(dir, "wal"), wlog.CompressionNone)
|
||||
require.NoError(b, err)
|
||||
b.Cleanup(func() { wal.Close() })
|
||||
|
||||
wbl, err := wlog.New(nil, nil, filepath.Join(dir, "wbl"), wlog.CompressionNone)
|
||||
require.NoError(b, err)
|
||||
b.Cleanup(func() { wbl.Close() })
|
||||
|
||||
// Load the WAL.
|
||||
for i := 0; i < b.N; i++ {
|
||||
opts := DefaultHeadOptions()
|
||||
opts.ChunkDirRoot = dir
|
||||
h, err := NewHead(nil, nil, wal, wbl, opts, nil)
|
||||
require.NoError(b, err)
|
||||
require.NoError(b, h.Init(0))
|
||||
}
|
||||
}
|
||||
|
||||
// TestHead_HighConcurrencyReadAndWrite generates 1000 series with a step of 15s and fills a whole block with samples,
|
||||
// this means in total it generates 4000 chunks because with a step of 15s there are 4 chunks per block per series.
|
||||
// While appending the samples to the head it concurrently queries them from multiple go routines and verifies that the
|
||||
|
@ -3484,6 +3509,93 @@ func TestWaitForPendingReadersInTimeRange(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestQueryOOOHeadDuringTruncate(t *testing.T) {
|
||||
const maxT int64 = 6000
|
||||
|
||||
dir := t.TempDir()
|
||||
opts := DefaultOptions()
|
||||
opts.EnableNativeHistograms = true
|
||||
opts.OutOfOrderTimeWindow = maxT
|
||||
opts.MinBlockDuration = maxT / 2 // So that head will compact up to 3000.
|
||||
|
||||
db, err := Open(dir, nil, nil, opts, nil)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, db.Close())
|
||||
})
|
||||
db.DisableCompactions()
|
||||
|
||||
var (
|
||||
ref = storage.SeriesRef(0)
|
||||
app = db.Appender(context.Background())
|
||||
)
|
||||
// Add in-order samples at every 100ms starting at 0ms.
|
||||
for i := int64(0); i < maxT; i += 100 {
|
||||
_, err := app.Append(ref, labels.FromStrings("a", "b"), i, 0)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// Add out-of-order samples at every 100ms starting at 50ms.
|
||||
for i := int64(50); i < maxT; i += 100 {
|
||||
_, err := app.Append(ref, labels.FromStrings("a", "b"), i, 0)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
|
||||
requireEqualOOOSamples(t, int(maxT/100-1), db)
|
||||
|
||||
// Synchronization points.
|
||||
allowQueryToStart := make(chan struct{})
|
||||
queryStarted := make(chan struct{})
|
||||
compactionFinished := make(chan struct{})
|
||||
|
||||
db.head.memTruncationCallBack = func() {
|
||||
// Compaction has started, let the query start and wait for it to actually start to simulate race condition.
|
||||
allowQueryToStart <- struct{}{}
|
||||
<-queryStarted
|
||||
}
|
||||
|
||||
go func() {
|
||||
db.Compact(context.Background()) // Compact and write blocks up to 3000 (maxtT/2).
|
||||
compactionFinished <- struct{}{}
|
||||
}()
|
||||
|
||||
// Wait for the compaction to start.
|
||||
<-allowQueryToStart
|
||||
|
||||
q, err := db.Querier(1500, 2500)
|
||||
require.NoError(t, err)
|
||||
queryStarted <- struct{}{} // Unblock the compaction.
|
||||
ctx := context.Background()
|
||||
|
||||
// Label names.
|
||||
res, annots, err := q.LabelNames(ctx, nil, labels.MustNewMatcher(labels.MatchEqual, "a", "b"))
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, annots)
|
||||
require.Equal(t, []string{"a"}, res)
|
||||
|
||||
// Label values.
|
||||
res, annots, err = q.LabelValues(ctx, "a", nil, labels.MustNewMatcher(labels.MatchEqual, "a", "b"))
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, annots)
|
||||
require.Equal(t, []string{"b"}, res)
|
||||
|
||||
// Samples
|
||||
ss := q.Select(ctx, false, nil, labels.MustNewMatcher(labels.MatchEqual, "a", "b"))
|
||||
require.True(t, ss.Next())
|
||||
s := ss.At()
|
||||
require.False(t, ss.Next()) // One series.
|
||||
it := s.Iterator(nil)
|
||||
require.NotEqual(t, chunkenc.ValNone, it.Next()) // Has some data.
|
||||
require.Equal(t, int64(1500), it.AtT()) // It is an in-order sample.
|
||||
require.NotEqual(t, chunkenc.ValNone, it.Next()) // Has some data.
|
||||
require.Equal(t, int64(1550), it.AtT()) // it is an out-of-order sample.
|
||||
require.NoError(t, it.Err())
|
||||
|
||||
require.NoError(t, q.Close()) // Cannot be deferred as the compaction waits for queries to close before finishing.
|
||||
|
||||
<-compactionFinished // Wait for compaction otherwise Go test finds stray goroutines.
|
||||
}
|
||||
|
||||
func TestAppendHistogram(t *testing.T) {
|
||||
l := labels.FromStrings("a", "b")
|
||||
for _, numHistograms := range []int{1, 10, 150, 200, 250, 300} {
|
||||
|
@ -6107,16 +6219,16 @@ func TestHeadAppender_AppendCTZeroSample(t *testing.T) {
|
|||
for _, tc := range []struct {
|
||||
name string
|
||||
appendableSamples []appendableSamples
|
||||
expectedSamples []model.Sample
|
||||
expectedSamples []chunks.Sample
|
||||
}{
|
||||
{
|
||||
name: "In order ct+normal sample",
|
||||
appendableSamples: []appendableSamples{
|
||||
{ts: 100, val: 10, ct: 1},
|
||||
},
|
||||
expectedSamples: []model.Sample{
|
||||
{Timestamp: 1, Value: 0},
|
||||
{Timestamp: 100, Value: 10},
|
||||
expectedSamples: []chunks.Sample{
|
||||
sample{t: 1, f: 0},
|
||||
sample{t: 100, f: 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -6125,10 +6237,10 @@ func TestHeadAppender_AppendCTZeroSample(t *testing.T) {
|
|||
{ts: 100, val: 10, ct: 1},
|
||||
{ts: 101, val: 10, ct: 1},
|
||||
},
|
||||
expectedSamples: []model.Sample{
|
||||
{Timestamp: 1, Value: 0},
|
||||
{Timestamp: 100, Value: 10},
|
||||
{Timestamp: 101, Value: 10},
|
||||
expectedSamples: []chunks.Sample{
|
||||
sample{t: 1, f: 0},
|
||||
sample{t: 100, f: 10},
|
||||
sample{t: 101, f: 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -6137,11 +6249,11 @@ func TestHeadAppender_AppendCTZeroSample(t *testing.T) {
|
|||
{ts: 100, val: 10, ct: 1},
|
||||
{ts: 102, val: 10, ct: 101},
|
||||
},
|
||||
expectedSamples: []model.Sample{
|
||||
{Timestamp: 1, Value: 0},
|
||||
{Timestamp: 100, Value: 10},
|
||||
{Timestamp: 101, Value: 0},
|
||||
{Timestamp: 102, Value: 10},
|
||||
expectedSamples: []chunks.Sample{
|
||||
sample{t: 1, f: 0},
|
||||
sample{t: 100, f: 10},
|
||||
sample{t: 101, f: 0},
|
||||
sample{t: 102, f: 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -6150,41 +6262,33 @@ func TestHeadAppender_AppendCTZeroSample(t *testing.T) {
|
|||
{ts: 100, val: 10, ct: 1},
|
||||
{ts: 101, val: 10, ct: 100},
|
||||
},
|
||||
expectedSamples: []model.Sample{
|
||||
{Timestamp: 1, Value: 0},
|
||||
{Timestamp: 100, Value: 10},
|
||||
{Timestamp: 101, Value: 10},
|
||||
expectedSamples: []chunks.Sample{
|
||||
sample{t: 1, f: 0},
|
||||
sample{t: 100, f: 10},
|
||||
sample{t: 101, f: 10},
|
||||
},
|
||||
},
|
||||
} {
|
||||
h, _ := newTestHead(t, DefaultBlockDuration, wlog.CompressionNone, false)
|
||||
defer func() {
|
||||
require.NoError(t, h.Close())
|
||||
}()
|
||||
a := h.Appender(context.Background())
|
||||
lbls := labels.FromStrings("foo", "bar")
|
||||
for _, sample := range tc.appendableSamples {
|
||||
_, err := a.AppendCTZeroSample(0, lbls, sample.ts, sample.ct)
|
||||
require.NoError(t, err)
|
||||
_, err = a.Append(0, lbls, sample.ts, sample.val)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, a.Commit())
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h, _ := newTestHead(t, DefaultBlockDuration, wlog.CompressionNone, false)
|
||||
defer func() {
|
||||
require.NoError(t, h.Close())
|
||||
}()
|
||||
a := h.Appender(context.Background())
|
||||
lbls := labels.FromStrings("foo", "bar")
|
||||
for _, sample := range tc.appendableSamples {
|
||||
_, err := a.AppendCTZeroSample(0, lbls, sample.ts, sample.ct)
|
||||
require.NoError(t, err)
|
||||
_, err = a.Append(0, lbls, sample.ts, sample.val)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, a.Commit())
|
||||
|
||||
q, err := NewBlockQuerier(h, math.MinInt64, math.MaxInt64)
|
||||
require.NoError(t, err)
|
||||
ss := q.Select(context.Background(), false, nil, labels.MustNewMatcher(labels.MatchEqual, "foo", "bar"))
|
||||
require.True(t, ss.Next())
|
||||
s := ss.At()
|
||||
require.False(t, ss.Next())
|
||||
it := s.Iterator(nil)
|
||||
for _, sample := range tc.expectedSamples {
|
||||
require.Equal(t, chunkenc.ValFloat, it.Next())
|
||||
timestamp, value := it.At()
|
||||
require.Equal(t, sample.Timestamp, model.Time(timestamp))
|
||||
require.Equal(t, sample.Value, model.SampleValue(value))
|
||||
}
|
||||
require.Equal(t, chunkenc.ValNone, it.Next())
|
||||
q, err := NewBlockQuerier(h, math.MinInt64, math.MaxInt64)
|
||||
require.NoError(t, err)
|
||||
result := query(t, q, labels.MustNewMatcher(labels.MatchEqual, "foo", "bar"))
|
||||
require.Equal(t, tc.expectedSamples, result[`{foo="bar"}`])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -509,7 +509,7 @@ type HeadAndOOOQuerier struct {
|
|||
head *Head
|
||||
index IndexReader
|
||||
chunkr ChunkReader
|
||||
querier storage.Querier
|
||||
querier storage.Querier // Used for LabelNames, LabelValues, but may be nil if head was truncated in the mean time, in which case we ignore it and not close it in the end.
|
||||
}
|
||||
|
||||
func NewHeadAndOOOQuerier(mint, maxt int64, head *Head, oooIsoState *oooIsolationState, querier storage.Querier) storage.Querier {
|
||||
|
@ -530,15 +530,24 @@ func NewHeadAndOOOQuerier(mint, maxt int64, head *Head, oooIsoState *oooIsolatio
|
|||
}
|
||||
|
||||
func (q *HeadAndOOOQuerier) LabelValues(ctx context.Context, name string, hints *storage.LabelHints, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) {
|
||||
if q.querier == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
return q.querier.LabelValues(ctx, name, hints, matchers...)
|
||||
}
|
||||
|
||||
func (q *HeadAndOOOQuerier) LabelNames(ctx context.Context, hints *storage.LabelHints, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) {
|
||||
if q.querier == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
return q.querier.LabelNames(ctx, hints, matchers...)
|
||||
}
|
||||
|
||||
func (q *HeadAndOOOQuerier) Close() error {
|
||||
q.chunkr.Close()
|
||||
if q.querier == nil {
|
||||
return nil
|
||||
}
|
||||
return q.querier.Close()
|
||||
}
|
||||
|
||||
|
|
|
@ -603,7 +603,7 @@ func (w *Watcher) readSegment(r *LiveReader, segmentNum int, tail bool) error {
|
|||
}
|
||||
|
||||
case record.Metadata:
|
||||
if !w.sendMetadata || !tail {
|
||||
if !w.sendMetadata {
|
||||
break
|
||||
}
|
||||
meta, err := dec.Metadata(rec, metadata[:0])
|
||||
|
|
|
@ -22,10 +22,10 @@ import (
|
|||
var minNormal = math.Float64frombits(0x0010000000000000) // The smallest positive normal value of type float64.
|
||||
|
||||
// Equal returns true if a and b differ by less than their sum
|
||||
// multiplied by epsilon.
|
||||
// multiplied by epsilon, or if both are StaleNaN, or if both are any other NaN.
|
||||
func Equal(a, b, epsilon float64) bool {
|
||||
// StaleNaN is a special value that is used as staleness maker, so
|
||||
// the two values are equal when both are exactly equals to stale NaN.
|
||||
// StaleNaN is a special value that is used as staleness maker, and
|
||||
// we don't want it to compare equal to any other NaN.
|
||||
if value.IsStaleNaN(a) || value.IsStaleNaN(b) {
|
||||
return value.IsStaleNaN(a) && value.IsStaleNaN(b)
|
||||
}
|
||||
|
|
50
util/almost/almost_test.go
Normal file
50
util/almost/almost_test.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2024 The Prometheus 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.
|
||||
|
||||
package almost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/prometheus/model/value"
|
||||
)
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
staleNaN := math.Float64frombits(value.StaleNaN)
|
||||
tests := []struct {
|
||||
a float64
|
||||
b float64
|
||||
epsilon float64
|
||||
want bool
|
||||
}{
|
||||
{0.0, 0.0, 0.0, true},
|
||||
{0.0, 0.1, 0.0, false},
|
||||
{1.0, 1.1, 0.1, true},
|
||||
{-1.0, -1.1, 0.1, true},
|
||||
{math.MaxFloat64, math.MaxFloat64 / 10, 0.1, false},
|
||||
{1.0, math.NaN(), 0.1, false},
|
||||
{math.NaN(), math.NaN(), 0.1, true},
|
||||
{math.NaN(), staleNaN, 0.1, false},
|
||||
{staleNaN, math.NaN(), 0.1, false},
|
||||
{staleNaN, staleNaN, 0.1, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("%v,%v,%v", tt.a, tt.b, tt.epsilon), func(t *testing.T) {
|
||||
if got := Equal(tt.a, tt.b, tt.epsilon); got != tt.want {
|
||||
t.Errorf("Equal(%v,%v,%v) = %v, want %v", tt.a, tt.b, tt.epsilon, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -59,16 +59,19 @@ import (
|
|||
"github.com/prometheus/prometheus/util/teststorage"
|
||||
)
|
||||
|
||||
var testEngine = promql.NewEngine(promql.EngineOpts{
|
||||
Logger: nil,
|
||||
Reg: nil,
|
||||
MaxSamples: 10000,
|
||||
Timeout: 100 * time.Second,
|
||||
NoStepSubqueryIntervalFn: func(int64) int64 { return 60 * 1000 },
|
||||
EnableAtModifier: true,
|
||||
EnableNegativeOffset: true,
|
||||
EnablePerStepStats: true,
|
||||
})
|
||||
func testEngine(t *testing.T) *promql.Engine {
|
||||
t.Helper()
|
||||
return promqltest.NewTestEngineWithOpts(t, promql.EngineOpts{
|
||||
Logger: nil,
|
||||
Reg: nil,
|
||||
MaxSamples: 10000,
|
||||
Timeout: 100 * time.Second,
|
||||
NoStepSubqueryIntervalFn: func(int64) int64 { return 60 * 1000 },
|
||||
EnableAtModifier: true,
|
||||
EnableNegativeOffset: true,
|
||||
EnablePerStepStats: true,
|
||||
})
|
||||
}
|
||||
|
||||
// testMetaStore satisfies the scrape.MetricMetadataStore interface.
|
||||
// It is used to inject specific metadata as part of a test case.
|
||||
|
@ -306,8 +309,7 @@ func (m *rulesRetrieverMock) CreateRuleGroups() {
|
|||
MaxSamples: 10,
|
||||
Timeout: 100 * time.Second,
|
||||
}
|
||||
|
||||
engine := promql.NewEngine(engineOpts)
|
||||
engine := promqltest.NewTestEngineWithOpts(m.testing, engineOpts)
|
||||
opts := &rules.ManagerOptions{
|
||||
QueryFunc: rules.EngineQueryFunc(engine, storage),
|
||||
Appendable: storage,
|
||||
|
@ -431,9 +433,10 @@ func TestEndpoints(t *testing.T) {
|
|||
|
||||
now := time.Now()
|
||||
|
||||
ng := testEngine(t)
|
||||
|
||||
t.Run("local", func(t *testing.T) {
|
||||
algr := rulesRetrieverMock{}
|
||||
algr.testing = t
|
||||
algr := rulesRetrieverMock{testing: t}
|
||||
|
||||
algr.CreateAlertingRules()
|
||||
algr.CreateRuleGroups()
|
||||
|
@ -445,7 +448,7 @@ func TestEndpoints(t *testing.T) {
|
|||
|
||||
api := &API{
|
||||
Queryable: storage,
|
||||
QueryEngine: testEngine,
|
||||
QueryEngine: ng,
|
||||
ExemplarQueryable: storage.ExemplarQueryable(),
|
||||
targetRetriever: testTargetRetriever.toFactory(),
|
||||
alertmanagerRetriever: testAlertmanagerRetriever{}.toFactory(),
|
||||
|
@ -496,8 +499,7 @@ func TestEndpoints(t *testing.T) {
|
|||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
algr := rulesRetrieverMock{}
|
||||
algr.testing = t
|
||||
algr := rulesRetrieverMock{testing: t}
|
||||
|
||||
algr.CreateAlertingRules()
|
||||
algr.CreateRuleGroups()
|
||||
|
@ -509,7 +511,7 @@ func TestEndpoints(t *testing.T) {
|
|||
|
||||
api := &API{
|
||||
Queryable: remote,
|
||||
QueryEngine: testEngine,
|
||||
QueryEngine: ng,
|
||||
ExemplarQueryable: storage.ExemplarQueryable(),
|
||||
targetRetriever: testTargetRetriever.toFactory(),
|
||||
alertmanagerRetriever: testAlertmanagerRetriever{}.toFactory(),
|
||||
|
@ -651,7 +653,7 @@ func TestQueryExemplars(t *testing.T) {
|
|||
|
||||
api := &API{
|
||||
Queryable: storage,
|
||||
QueryEngine: testEngine,
|
||||
QueryEngine: testEngine(t),
|
||||
ExemplarQueryable: storage.ExemplarQueryable(),
|
||||
}
|
||||
|
||||
|
@ -870,7 +872,7 @@ func TestStats(t *testing.T) {
|
|||
|
||||
api := &API{
|
||||
Queryable: storage,
|
||||
QueryEngine: testEngine,
|
||||
QueryEngine: testEngine(t),
|
||||
now: func() time.Time {
|
||||
return time.Unix(123, 0)
|
||||
},
|
||||
|
|
|
@ -32,6 +32,7 @@ import (
|
|||
"github.com/prometheus/prometheus/config"
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/promql/promqltest"
|
||||
"github.com/prometheus/prometheus/rules"
|
||||
"github.com/prometheus/prometheus/scrape"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
|
@ -86,7 +87,7 @@ func TestApiStatusCodes(t *testing.T) {
|
|||
"error from seriesset": errorTestQueryable{q: errorTestQuerier{s: errorTestSeriesSet{err: tc.err}}},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%s/%s", name, k), func(t *testing.T) {
|
||||
r := createPrometheusAPI(q)
|
||||
r := createPrometheusAPI(t, q)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/query?query=up", nil)
|
||||
|
@ -100,8 +101,10 @@ func TestApiStatusCodes(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func createPrometheusAPI(q storage.SampleAndChunkQueryable) *route.Router {
|
||||
engine := promql.NewEngine(promql.EngineOpts{
|
||||
func createPrometheusAPI(t *testing.T, q storage.SampleAndChunkQueryable) *route.Router {
|
||||
t.Helper()
|
||||
|
||||
engine := promqltest.NewTestEngineWithOpts(t, promql.EngineOpts{
|
||||
Logger: log.NewNopLogger(),
|
||||
Reg: nil,
|
||||
ActiveQueryTracker: nil,
|
||||
|
|
Loading…
Reference in a new issue