mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-06-23 18:48:03 +03:00
Compare commits
23 Commits
v1.102.10
...
test-templ
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e237e35c9c | ||
|
|
1645542a8a | ||
|
|
151eb1e4b6 | ||
|
|
5e4de8e860 | ||
|
|
6312d3bbba | ||
|
|
d2bede6b51 | ||
|
|
5ca5069fc4 | ||
|
|
8a3c460f63 | ||
|
|
ca653a515c | ||
|
|
e5b4cf33bf | ||
|
|
e24a8f2088 | ||
|
|
f27e120aeb | ||
|
|
ee1ce90501 | ||
|
|
47fe8cf3be | ||
|
|
5813aa6602 | ||
|
|
b4f4ece162 | ||
|
|
bb00f7529f | ||
|
|
ad3bd11334 | ||
|
|
875c6663ef | ||
|
|
b48b7c454a | ||
|
|
f523348b3f | ||
|
|
63bf1e008f | ||
|
|
419ac10c60 |
@@ -6,7 +6,7 @@ COPY web/ /build/
|
||||
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o web-amd64 github.com/VictoriMetrics/vmui/ && \
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o web-windows github.com/VictoriMetrics/vmui/
|
||||
|
||||
FROM alpine:3.21.0
|
||||
FROM alpine:3.21.2
|
||||
USER root
|
||||
|
||||
COPY --from=build-web-stage /build/web-amd64 /app/web
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
DOCKER_NAMESPACE ?= victoriametrics
|
||||
|
||||
ROOT_IMAGE ?= alpine:3.21.0
|
||||
ROOT_IMAGE ?= alpine:3.21.2
|
||||
ROOT_IMAGE_SCRATCH ?= scratch
|
||||
CERTS_IMAGE := alpine:3.21.0
|
||||
CERTS_IMAGE := alpine:3.21.2
|
||||
|
||||
GO_BUILDER_IMAGE := golang:1.23.4-alpine
|
||||
BUILDER_IMAGE := local/builder:2.0.0-$(shell echo $(GO_BUILDER_IMAGE) | tr :/ __)-1
|
||||
|
||||
@@ -4,7 +4,7 @@ services:
|
||||
# And forward them to --remoteWrite.url
|
||||
vmagent:
|
||||
container_name: vmagent
|
||||
image: victoriametrics/vmagent:v1.108.1
|
||||
image: victoriametrics/vmagent:v1.109.0
|
||||
depends_on:
|
||||
- "vminsert"
|
||||
ports:
|
||||
@@ -39,7 +39,7 @@ services:
|
||||
# where N is number of vmstorages (2 in this case).
|
||||
vmstorage-1:
|
||||
container_name: vmstorage-1
|
||||
image: victoriametrics/vmstorage:v1.108.1-cluster
|
||||
image: victoriametrics/vmstorage:v1.109.0-cluster
|
||||
ports:
|
||||
- 8482
|
||||
- 8400
|
||||
@@ -51,7 +51,7 @@ services:
|
||||
restart: always
|
||||
vmstorage-2:
|
||||
container_name: vmstorage-2
|
||||
image: victoriametrics/vmstorage:v1.108.1-cluster
|
||||
image: victoriametrics/vmstorage:v1.109.0-cluster
|
||||
ports:
|
||||
- 8482
|
||||
- 8400
|
||||
@@ -66,7 +66,7 @@ services:
|
||||
# pre-process them and distributes across configured vmstorage shards.
|
||||
vminsert:
|
||||
container_name: vminsert
|
||||
image: victoriametrics/vminsert:v1.108.1-cluster
|
||||
image: victoriametrics/vminsert:v1.109.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -81,7 +81,7 @@ services:
|
||||
# vmselect collects results from configured `--storageNode` shards.
|
||||
vmselect-1:
|
||||
container_name: vmselect-1
|
||||
image: victoriametrics/vmselect:v1.108.1-cluster
|
||||
image: victoriametrics/vmselect:v1.109.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -94,7 +94,7 @@ services:
|
||||
restart: always
|
||||
vmselect-2:
|
||||
container_name: vmselect-2
|
||||
image: victoriametrics/vmselect:v1.108.1-cluster
|
||||
image: victoriametrics/vmselect:v1.109.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -112,7 +112,7 @@ services:
|
||||
# It can be used as an authentication proxy.
|
||||
vmauth:
|
||||
container_name: vmauth
|
||||
image: victoriametrics/vmauth:v1.108.1
|
||||
image: victoriametrics/vmauth:v1.109.0
|
||||
depends_on:
|
||||
- "vmselect-1"
|
||||
- "vmselect-2"
|
||||
@@ -127,7 +127,7 @@ services:
|
||||
# vmalert executes alerting and recording rules
|
||||
vmalert:
|
||||
container_name: vmalert
|
||||
image: victoriametrics/vmalert:v1.108.1
|
||||
image: victoriametrics/vmalert:v1.109.0
|
||||
depends_on:
|
||||
- "vmauth"
|
||||
ports:
|
||||
|
||||
@@ -45,7 +45,7 @@ services:
|
||||
# storing logs and serving read queries.
|
||||
victorialogs:
|
||||
container_name: victorialogs
|
||||
image: victoriametrics/victoria-logs:v1.5.0-victorialogs
|
||||
image: victoriametrics/victoria-logs:v1.6.0-victorialogs
|
||||
command:
|
||||
- "--storageDataPath=/vlogs"
|
||||
- "--httpListenAddr=:9428"
|
||||
@@ -60,7 +60,7 @@ services:
|
||||
# scraping, storing metrics and serve read requests.
|
||||
victoriametrics:
|
||||
container_name: victoriametrics
|
||||
image: victoriametrics/victoria-metrics:v1.108.1
|
||||
image: victoriametrics/victoria-metrics:v1.109.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
volumes:
|
||||
@@ -79,7 +79,7 @@ services:
|
||||
# depending on the requested path.
|
||||
vmauth:
|
||||
container_name: vmauth
|
||||
image: victoriametrics/vmauth:v1.108.1
|
||||
image: victoriametrics/vmauth:v1.109.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
- "victorialogs"
|
||||
@@ -96,7 +96,7 @@ services:
|
||||
# vmalert executes alerting and recording rules according to given rule type.
|
||||
vmalert:
|
||||
container_name: vmalert
|
||||
image: victoriametrics/vmalert:v1.108.1
|
||||
image: victoriametrics/vmalert:v1.109.0
|
||||
depends_on:
|
||||
- "vmauth"
|
||||
- "alertmanager"
|
||||
|
||||
@@ -4,7 +4,7 @@ services:
|
||||
# And forward them to --remoteWrite.url
|
||||
vmagent:
|
||||
container_name: vmagent
|
||||
image: victoriametrics/vmagent:v1.108.1
|
||||
image: victoriametrics/vmagent:v1.109.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
@@ -22,7 +22,7 @@ services:
|
||||
# storing metrics and serve read requests.
|
||||
victoriametrics:
|
||||
container_name: victoriametrics
|
||||
image: victoriametrics/victoria-metrics:v1.108.1
|
||||
image: victoriametrics/victoria-metrics:v1.109.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
- 8089:8089
|
||||
@@ -65,7 +65,7 @@ services:
|
||||
# vmalert executes alerting and recording rules
|
||||
vmalert:
|
||||
container_name: vmalert
|
||||
image: victoriametrics/vmalert:v1.108.1
|
||||
image: victoriametrics/vmalert:v1.109.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
- "alertmanager"
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
# The alerts below are just recommendations and may require some updates
|
||||
# and threshold calibration according to every specific setup.
|
||||
groups:
|
||||
- name: alwayFiring
|
||||
rules:
|
||||
- alert: AlwaysFiring
|
||||
expr: up==1
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "job {{ $labels.job }} has an instance {{ $labels.instance | stripPort }}"
|
||||
instance: "{{ $labels.instance }}"
|
||||
description: "This alert is fine"
|
||||
- name: vm-health
|
||||
# note the `job` filter and update accordingly to your setup
|
||||
rules:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
# meta service will be ignored by compose
|
||||
.victorialogs:
|
||||
image: docker.io/victoriametrics/victoria-logs:v1.5.0-victorialogs
|
||||
image: docker.io/victoriametrics/victoria-logs:v1.6.0-victorialogs
|
||||
command:
|
||||
- -storageDataPath=/vlogs
|
||||
- -loggerFormat=json
|
||||
@@ -19,7 +19,7 @@ services:
|
||||
retries: 10
|
||||
|
||||
dd-proxy:
|
||||
image: docker.io/victoriametrics/vmauth:v1.108.1
|
||||
image: docker.io/victoriametrics/vmauth:v1.109.0
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- ./:/etc/vmauth
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
vmagent:
|
||||
container_name: vmagent
|
||||
image: victoriametrics/vmagent:v1.108.1
|
||||
image: victoriametrics/vmagent:v1.109.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
|
||||
victoriametrics:
|
||||
container_name: victoriametrics
|
||||
image: victoriametrics/victoria-metrics:v1.108.1
|
||||
image: victoriametrics/victoria-metrics:v1.109.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
volumes:
|
||||
@@ -50,7 +50,7 @@ services:
|
||||
|
||||
vmalert:
|
||||
container_name: vmalert
|
||||
image: victoriametrics/vmalert:v1.108.1
|
||||
image: victoriametrics/vmalert:v1.109.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
|
||||
@@ -3,7 +3,7 @@ version: "3"
|
||||
services:
|
||||
# Run `make package-victoria-logs` to build victoria-logs image
|
||||
vlogs:
|
||||
image: docker.io/victoriametrics/victoria-logs:v1.5.0-victorialogs
|
||||
image: docker.io/victoriametrics/victoria-logs:v1.6.0-victorialogs
|
||||
volumes:
|
||||
- vlogs:/vlogs
|
||||
ports:
|
||||
@@ -46,7 +46,7 @@ services:
|
||||
- "--config=/config.yml"
|
||||
|
||||
vmsingle:
|
||||
image: victoriametrics/victoria-metrics:v1.108.1
|
||||
image: victoriametrics/victoria-metrics:v1.109.0
|
||||
ports:
|
||||
- "8428:8428"
|
||||
command:
|
||||
|
||||
@@ -22,5 +22,5 @@ to [the latest available releases](https://docs.victoriametrics.com/changelog/).
|
||||
|
||||
## Currently supported LTS release lines
|
||||
|
||||
- v1.102.x - the latest one is [v1.102.9 LTS release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.9)
|
||||
- v1.97.x - the latest one is [v1.97.14 LTS release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.97.14)
|
||||
- v1.102.x - the latest one is [v1.102.10 LTS release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.10)
|
||||
- v1.97.x - the latest one is [v1.97.15 LTS release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.97.15)
|
||||
|
||||
@@ -16,7 +16,13 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta
|
||||
|
||||
## tip
|
||||
|
||||
* FEATURE: add [`histogram` stats function](https://docs.victoriametrics.com/victorialogs/logsql/#histogram-stats) for calculating [VictoriaMetrics histogram buckets](https://valyala.medium.com/improving-histogram-usability-for-prometheus-and-grafana-bc7e5df0e350) over the given [log field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model).
|
||||
## [v1.6.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.6.0-victorialogs)
|
||||
|
||||
Released at 2025-01-15
|
||||
|
||||
* FEATURE: add [`union` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#union-pipe), which can be used for returning results from multiple independent LogsQL queries.
|
||||
* FEATURE: add [`histogram` stats function](https://docs.victoriametrics.com/victorialogs/logsql/#histogram-stats) for calculating [VictoriaMetrics histogram buckets](https://valyala.medium.com/improving-histogram-usability-for-prometheus-and-grafana-bc7e5df0e350) over the given [log field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model). They will be used for building heatmaps at the [built-in Web UI](https://docs.victoriametrics.com/victorialogs/querying/#web-ui) and [VictoriaLogs plugin for Grafana](https://docs.victoriametrics.com/victorialogs/victorialogs-datasource/).
|
||||
* FEATURE: [`math` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#math-pipe): add `rand()` function, which can be used for generating random numbers in the range `[0 ... 1)`.
|
||||
|
||||
## [v1.5.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.5.0-victorialogs)
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ _time:5m error -(buggy_app OR foobar)
|
||||
|
||||
The parentheses are **required** here, since otherwise the query won't return the expected results.
|
||||
The query `error -buggy_app OR foobar` is interpreted as `(error AND NOT buggy_app) OR foobar` according to [priorities for AND, OR and NOT operator](#logical-filters).
|
||||
This query returns logs with `foobar` [word](#word), even if do not contain `error` word or contain `buggy_app` word.
|
||||
This query returns logs with `foobar` [word](#word), even if they do not contain `error` word or contain `buggy_app` word.
|
||||
So it is recommended wrapping the needed query parts into explicit parentheses if you are unsure in priority rules.
|
||||
As an additional bonus, explicit parentheses make queries easier to read and maintain.
|
||||
|
||||
@@ -1344,6 +1344,7 @@ LogsQL supports the following pipes:
|
||||
- [`stream_context`](#stream_context-pipe) allows selecting surrounding logs in front and after the matching logs
|
||||
per each [log stream](https://docs.victoriametrics.com/victorialogs/keyconcepts/#stream-fields).
|
||||
- [`top`](#top-pipe) returns top `N` field sets with the maximum number of matching logs.
|
||||
- [`union`](#union-pipe) returns results from multiple LogsQL queries.
|
||||
- [`uniq`](#uniq-pipe) returns unique log entires.
|
||||
- [`unpack_json`](#unpack_json-pipe) unpacks JSON messages from [log fields](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model).
|
||||
- [`unpack_logfmt`](#unpack_logfmt-pipe) unpacks [logfmt](https://brandur.org/logfmt) messages from [log fields](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model).
|
||||
@@ -1352,18 +1353,18 @@ LogsQL supports the following pipes:
|
||||
|
||||
### block_stats pipe
|
||||
|
||||
`<q> | block_stats` [pipe](#pipes) returns the following stats per each block processed by `<q>`. This pipe is needed mostly for debugging.
|
||||
|
||||
The returned per-block stats:
|
||||
`<q> | block_stats` [pipe](#pipes) returns the following stats per each block processed by `<q>` [query](#query-syntax):
|
||||
|
||||
- `field` - [field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model) name
|
||||
- `rows` - the number of rows at the given `field.
|
||||
- `rows` - the number of rows at the given `field`
|
||||
- `type` - internal storage type for the given `field`
|
||||
- `values_bytes` - on-disk size of the data for the given `field`
|
||||
- `bloom_bytes` - on-disk size of bloom filter data for the given `field`
|
||||
- `dict_bytes` - on-disk size of the dictionary data for the given `field`
|
||||
- `dict_items` - the number of unique values in the dictionary for the given `field`
|
||||
|
||||
The `block_stats` pipe is needed mostly for debugging purposes.
|
||||
|
||||
See also:
|
||||
|
||||
- [`value_type` filter](#value_type-filter)
|
||||
@@ -1507,10 +1508,10 @@ See also:
|
||||
|
||||
For example, the following query selects logs with the `error` [word](#word) for the last day,
|
||||
extracts ip address from [`_msg` field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#message-field) into `ip` field and then calculates top 10 ip addresses
|
||||
with the biggest number of logs:
|
||||
with the biggest number of logs using [`top` pipe](#top-pipe):
|
||||
|
||||
```logsql
|
||||
_time:1d error | extract "ip=<ip> " from _msg | stats by (ip) count() logs | sort by (logs) desc limit 10
|
||||
_time:1d error | extract "ip=<ip> " from _msg | top 10 (ip)
|
||||
```
|
||||
|
||||
It is expected that `_msg` field contains `ip=...` substring ending with space. For example, `error ip=1.2.3.4 from user_id=42`.
|
||||
@@ -1520,7 +1521,7 @@ If the `| extract ...` pipe is applied to [`_msg` field](https://docs.victoriame
|
||||
For example, the following query is equivalent to the previous one:
|
||||
|
||||
```logsql
|
||||
_time:1d error | extract "ip=<ip> " | stats by (ip) count() logs | sort by (logs) desc limit 10
|
||||
_time:1d error | extract "ip=<ip> " | top 10 (ip)
|
||||
```
|
||||
|
||||
If the `pattern` contains double quotes, then either put `\` in front of double quotes or put the `pattern` inside single quotes.
|
||||
@@ -1940,7 +1941,7 @@ Numeric fields can be transformed into the following string representation at `f
|
||||
- IPv4 - by adding `ipv4:` in front of the corresponding field name containing `uint32` representation of the IPv4 address.
|
||||
For example, `format "ip=<ipv4:ip_num>"`.
|
||||
|
||||
- Zero-padded 64-bit hex number - by adding 'hexnumencode:' in front of the corresponding field name. For example, `format "hex_num=<hexnumencode:some_field>"`.
|
||||
- Zero-padded 64-bit hex number - by adding `hexnumencode:` in front of the corresponding field name. For example, `format "hex_num=<hexnumencode:some_field>"`.
|
||||
|
||||
Add `keep_original_fields` to the end of `format ... as result_field` when the original non-empty value of the `result_field` must be preserved
|
||||
instead of overwriting it with the `format` results. For example, the following query adds formatted result to `foo` field only if it was missing or empty:
|
||||
@@ -2028,6 +2029,7 @@ _time:1d {app="app1"} | stats by (user) count() app1_hits
|
||||
|
||||
See also:
|
||||
|
||||
- [`in` filter](#multi-exact-filter)
|
||||
- [`stats` pipe](#stats-pipe)
|
||||
- [conditional `stats`](https://docs.victoriametrics.com/victorialogs/logsql/#stats-with-additional-filters)
|
||||
- [`filter` pipe](#filter-pipe)
|
||||
@@ -2063,7 +2065,7 @@ For example, the following query shows top 5 log entries with the maximum byte l
|
||||
logs for the last 5 minutes:
|
||||
|
||||
```logsql
|
||||
_time:5m | len(_msg) as msg_len | sort by (msg_len desc) | limit 1
|
||||
_time:5m | len(_msg) as msg_len | sort by (msg_len desc) | limit 5
|
||||
```
|
||||
|
||||
See also:
|
||||
@@ -2141,6 +2143,7 @@ The following mathematical operations are supported by `math` pipe:
|
||||
- `ln(arg)` - returns [natural logarithm](https://en.wikipedia.org/wiki/Natural_logarithm) for the given `arg`
|
||||
- `max(arg1, ..., argN)` - returns the maximum value among the given `arg1`, ..., `argN`
|
||||
- `min(arg1, ..., argN)` - returns the minimum value among the given `arg1`, ..., `argN`
|
||||
- `rand()` - returns pseudo-random number in the range `[0...1)`.
|
||||
- `round(arg)` - returns rounded to integer value for the given `arg`. The `round()` accepts optional `nearest` arg, which allows rounding the number to the given `nearest` multiple.
|
||||
For example, `round(temperature, 0.1)` rounds `temperature` field to one decimal digit after the point.
|
||||
|
||||
@@ -2764,6 +2767,21 @@ See also:
|
||||
- [`sort` pipe](#sort-pipe)
|
||||
- [`histogram` stats function](#histogram-stats)
|
||||
|
||||
### union pipe
|
||||
|
||||
`q1 | union (q2)` [pipe](#pipes) returns results of `q1` followed by results of `q2`. It works similar to `UNION ALL` in SQL.
|
||||
`q1` and `q2` may contain arbitrary [LogsQL queries](#logsql-tutorial).
|
||||
For example, the following query returns logs with `error` [word](#word) for the last 5 minutes, plus logs with `panic` word for the last hour:
|
||||
|
||||
```logsql
|
||||
_time:5m error | union (_time:1h panic)
|
||||
```
|
||||
|
||||
See also:
|
||||
|
||||
- [`join` pipe](#join-pipe)
|
||||
- [`in` filter](#multi-exact-filter)
|
||||
|
||||
### uniq pipe
|
||||
|
||||
`| uniq ...` [pipe](#pipes) returns unique results over the selected logs. For example, the following LogsQL query
|
||||
@@ -3257,7 +3275,7 @@ See also:
|
||||
|
||||
### histogram stats
|
||||
|
||||
`histogram(field)` [stats pipe function](#stats-pipe-functions) returns [VictoriaMetrics histogram buckets](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model)
|
||||
`histogram(field)` [stats pipe function](#stats-pipe-functions) returns [VictoriaMetrics histogram buckets](https://valyala.medium.com/improving-histogram-usability-for-prometheus-and-grafana-bc7e5df0e350)
|
||||
for the given [`field`](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model).
|
||||
|
||||
For example, the following query returns histogram buckets for the `response_size` field grouped by `host` field, across logs for the last 5 minutes:
|
||||
|
||||
@@ -33,8 +33,8 @@ Just download archive for the needed Operating system and architecture, unpack i
|
||||
For example, the following commands download VictoriaLogs archive for Linux/amd64, unpack and run it:
|
||||
|
||||
```sh
|
||||
curl -L -O https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.5.0-victorialogs/victoria-logs-linux-amd64-v1.5.0-victorialogs.tar.gz
|
||||
tar xzf victoria-logs-linux-amd64-v1.5.0-victorialogs.tar.gz
|
||||
curl -L -O https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.6.0-victorialogs/victoria-logs-linux-amd64-v1.6.0-victorialogs.tar.gz
|
||||
tar xzf victoria-logs-linux-amd64-v1.6.0-victorialogs.tar.gz
|
||||
./victoria-logs-prod
|
||||
```
|
||||
|
||||
@@ -58,7 +58,7 @@ Here is the command to run VictoriaLogs in a Docker container:
|
||||
|
||||
```sh
|
||||
docker run --rm -it -p 9428:9428 -v ./victoria-logs-data:/victoria-logs-data \
|
||||
docker.io/victoriametrics/victoria-logs:v1.5.0-victorialogs
|
||||
docker.io/victoriametrics/victoria-logs:v1.6.0-victorialogs
|
||||
```
|
||||
|
||||
See also:
|
||||
|
||||
@@ -24,11 +24,11 @@ the returned logs by some field (usually [`_time` field](https://docs.victoriame
|
||||
_time:5m | sort by (_time)
|
||||
```
|
||||
|
||||
If the number of returned logs is too big, it may be limited with the [`last` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#last-pipe).
|
||||
If the number of returned logs is too big, it may be limited with the [`first` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#first-pipe).
|
||||
For example, the following query returns 10 most recent logs, which were ingested during the last 5 minutes:
|
||||
|
||||
```logsql
|
||||
_time:5m | last 10 by (_time)
|
||||
_time:5m | first 10 by (_time desc)
|
||||
```
|
||||
|
||||
See also:
|
||||
@@ -358,7 +358,7 @@ query returns top 10 `/24` subnetworks with the biggest number of logs for the l
|
||||
_time:5m | stats by (ip:/24) count() rows | last 10 by (rows)
|
||||
```
|
||||
|
||||
This query uses [`last` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#last-pipe) in order to get up to 10 per-subnetwork stats
|
||||
This query uses [`first` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#first-pipe) in order to get up to 10 per-subnetwork stats
|
||||
with the biggest number of rows.
|
||||
|
||||
The query assumes the original logs have `ip` [field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model) with the IPv4 address.
|
||||
@@ -369,7 +369,7 @@ extracts IPv4 address from [`_msg` field](https://docs.victoriametrics.com/victo
|
||||
`/16` subnetworks with the biggest number of logs for the last 5 minutes:
|
||||
|
||||
```logsql
|
||||
_time:5m | extract_regexp "(?P<ip>([0-9]+[.]){3}[0-9]+)" | stats by (ip:/16) count() rows | last 10 by (rows)
|
||||
_time:5m | extract_regexp "(?P<ip>([0-9]+[.]){3}[0-9]+)" | stats by (ip:/16) count() rows | first 10 by (rows desc)
|
||||
```
|
||||
|
||||
## How to calculate the number of logs per every value of the given field?
|
||||
@@ -407,12 +407,12 @@ _time:5m | uniq by (host, path)
|
||||
|
||||
## How to return last N logs for the given query?
|
||||
|
||||
Use [`last` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#last-pipe). For example, the following query returns the last 10 logs with the `error`
|
||||
Use [`first` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#first-pipe). For example, the following query returns the last 10 logs with the `error`
|
||||
[word](https://docs.victoriametrics.com/victorialogs/logsql/#word) in the [`_msg` field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#message-field)
|
||||
over the logs for the last 5 minutes:
|
||||
|
||||
```logsql
|
||||
_time:5m error | last 10 by (_time)
|
||||
_time:5m error | first 10 by (_time desc)
|
||||
```
|
||||
|
||||
It sorts the matching logs by [`_time` field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#time-field) and then selects
|
||||
|
||||
@@ -18,22 +18,16 @@ It has the following features:
|
||||
- It supports live tailing - see [these docs](#live-tailing).
|
||||
|
||||
This tool can be obtained from the linked release pages at the [changelog](https://docs.victoriametrics.com/victorialogs/changelog/)
|
||||
or from [docker images](https://hub.docker.com/r/victoriametrics/vlogscli/tags):
|
||||
or from [docker images](https://hub.docker.com/r/victoriametrics/vlogscli/tags).
|
||||
|
||||
### Running `vlogscli` from release binary
|
||||
|
||||
```sh
|
||||
curl -L -O https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.5.0-victorialogs/vlogscli-linux-amd64-v1.5.0-victorialogs.tar.gz
|
||||
tar xzf vlogscli-linux-amd64-v1.5.0-victorialogs.tar.gz
|
||||
curl -L -O https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.6.0-victorialogs/vlogscli-linux-amd64-v1.6.0-victorialogs.tar.gz
|
||||
tar xzf vlogscli-linux-amd64-v1.6.0-victorialogs.tar.gz
|
||||
./vlogscli-prod
|
||||
```
|
||||
|
||||
### Running `vlogscli` from Docker image
|
||||
|
||||
```sh
|
||||
docker run --rm -it docker.io/victoriametrics/vlogscli:v1.5.0-victorialogs
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
By default `vlogscli` sends queries to [`http://localhost:8429/select/logsql/query`](https://docs.victoriametrics.com/victorialogs/querying/#querying-logs).
|
||||
|
||||
@@ -75,7 +75,7 @@ The `push_frequency` parameter{{% available_from "v1.18.7" anomaly %}} (default
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`url`
|
||||
<span style="white-space: nowrap;">`url`</span>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
@@ -86,7 +86,7 @@ Link where to push metrics to. Example: `"http://localhost:8480/"`
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`tenant_id`
|
||||
<span style="white-space: nowrap;">`tenant_id`</span>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
@@ -97,7 +97,7 @@ Tenant ID for cluster version. Example: `"0:0"`
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`health_path`
|
||||
<span style="white-space: nowrap;">`health_path`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -111,7 +111,7 @@ Tenant ID for cluster version. Example: `"0:0"`
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`user`
|
||||
<span style="white-space: nowrap;">`user`</span>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>BasicAuth username</td>
|
||||
@@ -119,7 +119,7 @@ Tenant ID for cluster version. Example: `"0:0"`
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`password`
|
||||
<span style="white-space: nowrap;">`password`</span>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>BasicAuth password</td>
|
||||
@@ -127,7 +127,7 @@ Tenant ID for cluster version. Example: `"0:0"`
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`bearer_token`
|
||||
<span style="white-space: nowrap;">`bearer_token`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -140,7 +140,7 @@ Token is passed in the standard format with header: `Authorization: bearer {toke
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`bearer_token_file`
|
||||
<span style="white-space: nowrap;">`bearer_token_file`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -153,7 +153,7 @@ Path to a file, which contains token, that is passed in the standard format with
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`verify_tls`
|
||||
<span style="white-space: nowrap;">`verify_tls`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -168,11 +168,11 @@ If a path to a CA bundle file (like `ca.crt`), it will verify the certificate us
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`tls_cert_file`
|
||||
<span style="white-space: nowrap;">`tls_cert_file`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`path/to/cert.crt`
|
||||
<span style="white-space: nowrap;">`path/to/cert.crt`</span>
|
||||
</td>
|
||||
<td>
|
||||
Path to a file with the client certificate, i.e. `client.crt`{{% available_from "v1.16.3" anomaly %}}.
|
||||
@@ -181,7 +181,7 @@ Path to a file with the client certificate, i.e. `client.crt`{{% available_from
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`tls_key_file`
|
||||
<span style="white-space: nowrap;">`tls_key_file`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -194,7 +194,7 @@ Path to a file with the client certificate key, i.e. `client.key`{{% available_f
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`timeout`
|
||||
<span style="white-space: nowrap;">`timeout`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -205,7 +205,7 @@ Path to a file with the client certificate key, i.e. `client.key`{{% available_f
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`push_frequency`
|
||||
<span style="white-space: nowrap;">`push_frequency`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -216,7 +216,7 @@ Path to a file with the client certificate key, i.e. `client.key`{{% available_f
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`extra_labels`
|
||||
<span style="white-space: nowrap;">`extra_labels`</span>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>Section for custom labels specified by user.</td>
|
||||
@@ -271,7 +271,7 @@ For detailed guidance on configuring mTLS parameters such as `verify_tls`, `tls_
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_start_time_seconds`
|
||||
<span style="white-space: nowrap;">`vmanomaly_start_time_seconds`</span>
|
||||
</td>
|
||||
<td>Gauge</td>
|
||||
<td>vmanomaly start time in UNIX time</td>
|
||||
@@ -279,7 +279,7 @@ For detailed guidance on configuring mTLS parameters such as `verify_tls`, `tls_
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_version_info`
|
||||
<span style="white-space: nowrap;">`vmanomaly_version_info`</span>
|
||||
</td>
|
||||
<td>Gauge</td>
|
||||
<td>vmanomaly version information, contained in `version` label{{% available_from "v1.17.2" anomaly %}}.</td>
|
||||
@@ -287,7 +287,7 @@ For detailed guidance on configuring mTLS parameters such as `verify_tls`, `tls_
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_ui_version_info`
|
||||
<span style="white-space: nowrap;">`vmanomaly_ui_version_info`</span>
|
||||
</td>
|
||||
<td>Gauge</td>
|
||||
<td>vmanomaly UI version information, contained in `version` label{{% available_from "v1.17.2" anomaly %}}.</td>
|
||||
@@ -295,7 +295,7 @@ For detailed guidance on configuring mTLS parameters such as `verify_tls`, `tls_
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_available_memory_bytes`
|
||||
<span style="white-space: nowrap;">`vmanomaly_available_memory_bytes`</span>
|
||||
</td>
|
||||
<td>Gauge</td>
|
||||
<td>Virtual memory size in bytes, available to the process{{% available_from "v1.18.4" anomaly %}}.</td>
|
||||
@@ -303,7 +303,7 @@ For detailed guidance on configuring mTLS parameters such as `verify_tls`, `tls_
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_cpu_cores_available`
|
||||
<span style="white-space: nowrap;">`vmanomaly_cpu_cores_available`</span>
|
||||
</td>
|
||||
<td>Gauge</td>
|
||||
<td>Number of (logical) CPU cores available to the process{{% available_from "v1.18.4" anomaly %}}.</td>
|
||||
@@ -331,9 +331,11 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_reader_request_duration_seconds`
|
||||
<span style="white-space: nowrap;">`vmanomaly_reader_request_duration_seconds`</span>
|
||||
</td>
|
||||
<td>`Histogram` (was `Summary`{{% deprecated_from "v1.17.0" anomaly %}})</td>
|
||||
<td>
|
||||
|
||||
<span style="white-space: nowrap;">`Histogram`</span> (was `Summary`{{% deprecated_from "v1.17.0" anomaly %}})</td>
|
||||
<td>The total time (in seconds) taken by queries to VictoriaMetrics `url` for the `query_key` query within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -343,9 +345,12 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_reader_responses` (named `vmanomaly_reader_response_count`{{% deprecated_from "v1.17.0" anomaly %}})
|
||||
<span style="white-space: nowrap;">`vmanomaly_reader_responses`</span> (named `vmanomaly_reader_response_count`{{% deprecated_from "v1.17.0" anomaly %}})
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The count of responses received from VictoriaMetrics `url` for the `query_key` query, categorized by `code`, within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -355,21 +360,27 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_reader_received_bytes`
|
||||
<span style="white-space: nowrap;">`vmanomaly_reader_received_bytes`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The total number of bytes received in responses for the `query_key` query within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
`url`, `query_key`, `scheduler_alias`, `preset`
|
||||
`url`, `query_key`, <span style="white-space: nowrap;">`scheduler_alias`</span>, `preset`
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_reader_response_parsing_seconds`
|
||||
<span style="white-space: nowrap;">`vmanomaly_reader_response_parsing_seconds`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Histogram` (was `Summary`{{% deprecated_from "v1.17.0" anomaly %}})
|
||||
</td>
|
||||
<td>`Histogram` (was `Summary`{{% deprecated_from "v1.17.0" anomaly %}}</td>
|
||||
<td>The total time (in seconds) taken for data parsing at each `step` (json, dataframe) for the `query_key` query within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -379,9 +390,12 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_reader_timeseries_received`
|
||||
<span style="white-space: nowrap;">`vmanomaly_reader_timeseries_received`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The total number of timeseries received from VictoriaMetrics for the `query_key` query within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -391,9 +405,12 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_reader_datapoints_received`
|
||||
<span style="white-space: nowrap;">`vmanomaly_reader_datapoints_received`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The total number of datapoints received from VictoriaMetrics for the `query_key` query within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -425,9 +442,12 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_model_runs`
|
||||
<span style="white-space: nowrap;">`vmanomaly_model_runs`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>How many successful `stage` (`fit`, `infer`, `fit_infer`) runs occurred for models of class `model_alias` based on results from the `query_key` query, within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -437,9 +457,11 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_model_run_duration_seconds`
|
||||
<span style="white-space: nowrap;">`vmanomaly_model_run_duration_seconds`</span>
|
||||
</td>
|
||||
<td>`Histogram` (was `Summary`{{% deprecated_from "v1.17.0" anomaly %}}) </td>
|
||||
<td>
|
||||
|
||||
<span style="white-space: nowrap;">`Histogram`</span> (was `Summary`{{% deprecated_from "v1.17.0" anomaly %}}) </td>
|
||||
<td>The total time (in seconds) taken by model invocations during the `stage` (`fit`, `infer`, `fit_infer`), based on the results of the `query_key` query, for models of class `model_alias`, within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -449,9 +471,12 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_model_datapoints_accepted`
|
||||
<span style="white-space: nowrap;">`vmanomaly_model_datapoints_accepted`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The number of datapoints accepted (excluding NaN or Inf values) by models of class `model_alias` from the results of the `query_key` query during the `stage` (`infer`, `fit_infer`), within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -461,9 +486,12 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_model_datapoints_produced`
|
||||
<span style="white-space: nowrap;">`vmanomaly_model_datapoints_produced`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The number of datapoints generated by models of class `model_alias` during the `stage` (`infer`, `fit_infer`) based on results from the `query_key` query, within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -473,9 +501,12 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_models_active`
|
||||
<span style="white-space: nowrap;">`vmanomaly_models_active`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Gauge`
|
||||
</td>
|
||||
<td>`Gauge`</td>
|
||||
<td>The number of model instances of class `model_alias` currently available for inference for the `query_key` query, within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -485,9 +516,12 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_model_runs_skipped`
|
||||
<span style="white-space: nowrap;">`vmanomaly_model_runs_skipped`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The number of times model runs (of class `model_alias`) were skipped in expected situations (e.g., no data for fitting/inference, or no new data to infer on) during the `stage` (`fit`, `infer`, `fit_infer`), based on results from the `query_key` query, within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -497,12 +531,15 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_model_run_errors`
|
||||
<span style="white-space: nowrap;">`vmanomaly_model_run_errors`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The number of times model runs (of class `model_alias`) failed due to internal service errors during the `stage` (`fit`, `infer`, `fit_infer`), based on results from the `query_key` query, within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
`stage`, `query_key`, `model_alias`, `scheduler_alias`, `preset`
|
||||
`stage`, `query_key`, `model_alias`, <span style="white-space: nowrap;">`scheduler_alias`</span>, `preset`
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -528,22 +565,28 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_writer_request_duration_seconds`
|
||||
<span style="white-space: nowrap;">`vmanomaly_writer_request_duration_seconds`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Histogram` (was `Summary`{{% deprecated_from "v1.17.0" anomaly %}})
|
||||
</td>
|
||||
<td>`Histogram` (was `Summary`{{% deprecated_from "v1.17.0" anomaly %}})</td>
|
||||
<td>The total time (in seconds) taken by write requests to VictoriaMetrics `url` for the `query_key` query within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`url`, `query_key`, `scheduler_alias`, `preset`
|
||||
`url`, `query_key`, <span style="white-space: nowrap;">`scheduler_alias`</span>, `preset`
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_writer_responses` (named `vmanomaly_reader_response_count`{{% deprecated_from "v1.17.0" anomaly %}})
|
||||
<span style="white-space: nowrap;">`vmanomaly_writer_responses`</span> (named `vmanomaly_reader_response_count`{{% deprecated_from "v1.17.0" anomaly %}})
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The count of response codes received from VictoriaMetrics `url` for the `query_key` query, categorized by `code`, within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.
|
||||
</td>
|
||||
<td>
|
||||
@@ -554,9 +597,12 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_writer_sent_bytes`
|
||||
<span style="white-space: nowrap;">`vmanomaly_writer_sent_bytes`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The total number of bytes sent to VictoriaMetrics `url` for the `query_key` query within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -566,9 +612,11 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_writer_request_serialize_seconds`
|
||||
<span style="white-space: nowrap;">`vmanomaly_writer_request_serialize_seconds`</span>
|
||||
</td>
|
||||
<td>`Histogram` (was `Summary`{{% deprecated_from "v1.17.0" anomaly %}}</td>
|
||||
<td>
|
||||
|
||||
<span style="white-space: nowrap;">`Histogram`</span> (was `Summary`{{% deprecated_from "v1.17.0" anomaly %}})</td>
|
||||
<td>The total time (in seconds) taken for serializing data for the `query_key` query within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -578,9 +626,12 @@ Label names [description](#labelnames)
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`vmanomaly_writer_datapoints_sent`
|
||||
<span style="white-space: nowrap;">`vmanomaly_writer_datapoints_sent`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The total number of datapoints sent to VictoriaMetrics for the `query_key` query within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
@@ -589,9 +640,13 @@ Label names [description](#labelnames)
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
`vmanomaly_writer_timeseries_sent`
|
||||
|
||||
<span style="white-space: nowrap;">`vmanomaly_writer_timeseries_sent`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`Counter`
|
||||
</td>
|
||||
<td>`Counter`</td>
|
||||
<td>The total number of timeseries sent to VictoriaMetrics for the `query_key` query within the specified scheduler `scheduler_alias`, in the `vmanomaly` service running in `preset` mode.</td>
|
||||
<td>
|
||||
|
||||
|
||||
@@ -105,9 +105,10 @@ reader:
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`class`
|
||||
<span style="white-space: nowrap;">`class`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`reader.vm.VmReader` (or `vm`{{% available_from "v1.13.0" anomaly %}})
|
||||
</td>
|
||||
<td>
|
||||
@@ -117,7 +118,7 @@ Name of the class needed to enable reading from VictoriaMetrics or Prometheus. V
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`queries`
|
||||
<span style="white-space: nowrap;">`queries`</span>
|
||||
</td>
|
||||
<td>
|
||||
See [per-query config example](#per-query-config-example) above
|
||||
@@ -129,10 +130,11 @@ See [per-query config section](#per-query-parameters) above
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`datasource_url`
|
||||
<span style="white-space: nowrap;">`datasource_url`</span>
|
||||
</td>
|
||||
<td>
|
||||
`http://localhost:8481/`
|
||||
|
||||
<span style="white-space: nowrap;">`http://localhost:8481/`</span>
|
||||
</td>
|
||||
<td>
|
||||
Datasource URL address
|
||||
@@ -141,7 +143,7 @@ Datasource URL address
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`tenant_id`
|
||||
<span style="white-space: nowrap;">`tenant_id`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -154,7 +156,7 @@ For VictoriaMetrics Cluster version only, tenants are identified by `accountID`
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`sampling_period`
|
||||
<span style="white-space: nowrap;">`sampling_period`</span>
|
||||
</td>
|
||||
<td>
|
||||
`1h`
|
||||
@@ -166,10 +168,11 @@ Frequency of the points returned. Will be converted to `/query_range?step=%s` pa
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`query_range_path`
|
||||
<span style="white-space: nowrap;">`query_range_path`</span>
|
||||
</td>
|
||||
<td>
|
||||
`/api/v1/query_range`
|
||||
|
||||
<span style="white-space: nowrap;">`/api/v1/query_range`</span>
|
||||
</td>
|
||||
<td>
|
||||
Performs PromQL/MetricsQL range query
|
||||
@@ -178,7 +181,7 @@ Performs PromQL/MetricsQL range query
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`health_path`
|
||||
<span style="white-space: nowrap;">`health_path`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -191,7 +194,7 @@ Absolute or relative URL address where to check availability of the datasource.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`user`
|
||||
<span style="white-space: nowrap;">`user`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -204,7 +207,7 @@ BasicAuth username
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`password`
|
||||
<span style="white-space: nowrap;">`password`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -217,7 +220,7 @@ BasicAuth password
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`timeout`
|
||||
<span style="white-space: nowrap;">`timeout`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -230,7 +233,7 @@ Timeout for the requests, passed as a string
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`verify_tls`
|
||||
<span style="white-space: nowrap;">`verify_tls`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -245,7 +248,7 @@ If a path to a CA bundle file (like `ca.crt`), it will verify the certificate us
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`tls_cert_file`
|
||||
<span style="white-space: nowrap;">`tls_cert_file`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -258,7 +261,7 @@ Path to a file with the client certificate, i.e. `client.crt`{{% available_from
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`tls_key_file`
|
||||
<span style="white-space: nowrap;">`tls_key_file`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -271,7 +274,7 @@ Path to a file with the client certificate key, i.e. `client.key`{{% available_f
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`bearer_token`
|
||||
<span style="white-space: nowrap;">`bearer_token`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -284,7 +287,7 @@ Token is passed in the standard format with header: `Authorization: bearer {toke
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`bearer_token_file`
|
||||
<span style="white-space: nowrap;">`bearer_token_file`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -297,7 +300,7 @@ Path to a file, which contains token, that is passed in the standard format with
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`extra_filters`
|
||||
<span style="white-space: nowrap;">`extra_filters`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -310,7 +313,7 @@ List of strings with series selector. See: [Prometheus querying API enhancements
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`query_from_last_seen_timestamp`
|
||||
<span style="white-space: nowrap;">`query_from_last_seen_timestamp`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -323,7 +326,7 @@ If True, then query will be performed from the last seen timestamp for a given s
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`latency_offset`
|
||||
<span style="white-space: nowrap;">`latency_offset`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -336,7 +339,7 @@ It allows overriding the default `-search.latencyOffset`{{% available_from "v1.1
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`max_points_per_query`
|
||||
<span style="white-space: nowrap;">`max_points_per_query`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -349,7 +352,7 @@ Optional arg{{% available_from "v1.17.0" anomaly %}} overrides how `search.maxPo
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`tz`
|
||||
<span style="white-space: nowrap;">`tz`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -362,7 +365,7 @@ Optional argument{{% available_from "v1.18.0" anomaly %}} specifies the [IANA](h
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`data_range`
|
||||
<span style="white-space: nowrap;">`data_range`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ Examples: `"50s"`, `"4m"`, `"3h"`, `"2d"`, `"1w"`.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`fit_window`
|
||||
<span style="white-space: nowrap;">`fit_window`</span>
|
||||
</td>
|
||||
<td>str</td>
|
||||
<td>
|
||||
@@ -128,7 +128,7 @@ Examples: `"50s"`, `"4m"`, `"3h"`, `"2d"`, `"1w"`.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`infer_every`
|
||||
<span style="white-space: nowrap;">`infer_every`</span>
|
||||
</td>
|
||||
<td>str</td>
|
||||
<td>
|
||||
@@ -140,7 +140,7 @@ Examples: `"50s"`, `"4m"`, `"3h"`, `"2d"`, `"1w"`.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`fit_every`
|
||||
<span style="white-space: nowrap;">`fit_every`</span>
|
||||
</td>
|
||||
<td>str, Optional</td>
|
||||
<td>
|
||||
@@ -155,14 +155,12 @@ How often to completely retrain the models. If not set, value of `infer_every` i
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
<span>
|
||||
`start_from`{{% available_from "v1.18.5" anomaly %}}
|
||||
</span>
|
||||
<span style="white-space: nowrap;">`start_from`{{% available_from "v1.18.5" anomaly %}}</span>
|
||||
</td>
|
||||
<td>str, Optional</td>
|
||||
<td>str, <span style="white-space: nowrap;">Optional</span></td>
|
||||
<td>
|
||||
|
||||
`2024-11-26T01:00:00Z`, `01:00`
|
||||
<span style="white-space: nowrap;">`2024-11-26T01:00:00Z`</span>, `01:00`
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -171,11 +169,10 @@ Specifies when to initiate the first `fit_every` call. Accepts either an ISO 860
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span>
|
||||
`tz`{{% available_from "v1.18.5" anomaly %}}
|
||||
</span>
|
||||
|
||||
<span style="white-space: nowrap;">`tz`{{% available_from "v1.18.5" anomaly %}}</span>
|
||||
</td>
|
||||
<td>str, Optional</td>
|
||||
<td>str, <span style="white-space: nowrap;">Optional</span></td>
|
||||
<td>
|
||||
|
||||
`America/New_York`
|
||||
@@ -232,7 +229,7 @@ If a time zone is omitted, a timezone-naive datetime is used.
|
||||
<td>ISO 8601</td>
|
||||
<td>
|
||||
|
||||
`fit_start_iso`
|
||||
<span style="white-space: nowrap;">`fit_start_iso`</span>
|
||||
</td>
|
||||
<td>str</td>
|
||||
<td>
|
||||
@@ -245,16 +242,19 @@ If a time zone is omitted, a timezone-naive datetime is used.
|
||||
<td>UNIX time</td>
|
||||
<td>
|
||||
|
||||
`fit_start_s`
|
||||
<span style="white-space: nowrap;">`fit_start_s`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
<span style="white-space: nowrap;">float</span>
|
||||
</td>
|
||||
<td>float</td>
|
||||
<td>1648771200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ISO 8601</td>
|
||||
<td>
|
||||
|
||||
`fit_end_iso`
|
||||
<span style="white-space: nowrap;">`fit_end_iso`</span>
|
||||
</td>
|
||||
<td>str</td>
|
||||
<td>
|
||||
@@ -270,7 +270,7 @@ If a time zone is omitted, a timezone-naive datetime is used.
|
||||
<td>UNIX time</td>
|
||||
<td>
|
||||
|
||||
`fit_end_s`
|
||||
<span style="white-space: nowrap;">`fit_end_s`</span>
|
||||
</td>
|
||||
<td>float</td>
|
||||
<td>1649548800</td>
|
||||
@@ -294,7 +294,7 @@ If a time zone is omitted, a timezone-naive datetime is used.
|
||||
<td>ISO 8601</td>
|
||||
<td>
|
||||
|
||||
`infer_start_iso`
|
||||
<span style="white-space: nowrap;">`infer_start_iso`</span>
|
||||
</td>
|
||||
<td>str</td>
|
||||
<td>
|
||||
@@ -307,16 +307,19 @@ If a time zone is omitted, a timezone-naive datetime is used.
|
||||
<td>UNIX time</td>
|
||||
<td>
|
||||
|
||||
`infer_start_s`
|
||||
<span style="white-space: nowrap;">`infer_start_s`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
<span style="white-space: nowrap;">float</span>
|
||||
</td>
|
||||
<td>float</td>
|
||||
<td>1649635200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ISO 8601</td>
|
||||
<td>
|
||||
|
||||
`infer_end_iso`
|
||||
<span style="white-space: nowrap;">`infer_end_iso`</span>
|
||||
</td>
|
||||
<td>str</td>
|
||||
<td>
|
||||
@@ -332,7 +335,7 @@ If a time zone is omitted, a timezone-naive datetime is used.
|
||||
<td>UNIX time</td>
|
||||
<td>
|
||||
|
||||
`infer_end_s`
|
||||
<span style="white-space: nowrap;">`infer_end_s`</span>
|
||||
</td>
|
||||
<td>float</td>
|
||||
<td>1649894400</td>
|
||||
@@ -381,9 +384,18 @@ If a time zone is omitted, a timezone-naive datetime is used.
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Type</th>
|
||||
<th>Example</th>
|
||||
<th>
|
||||
|
||||
<span style="white-space: nowrap;">Parameter</span>
|
||||
</th>
|
||||
<th>
|
||||
|
||||
<span style="white-space: nowrap;">Type</span>
|
||||
</th>
|
||||
<th>
|
||||
|
||||
<span style="white-space: nowrap;">Example</span>
|
||||
</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -391,7 +403,7 @@ If a time zone is omitted, a timezone-naive datetime is used.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`n_jobs`
|
||||
<span style="white-space: nowrap;">`n_jobs`</span>
|
||||
</td>
|
||||
<td>int</td>
|
||||
<td>
|
||||
@@ -412,10 +424,22 @@ This timeframe will be used for slicing on intervals `(fit_window, infer_window
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Format</th>
|
||||
<th>Parameter</th>
|
||||
<th>Type</th>
|
||||
<th>Example</th>
|
||||
<th>
|
||||
|
||||
<span style="white-space: nowrap;">Format</span>
|
||||
</th>
|
||||
<th>
|
||||
|
||||
<span style="white-space: nowrap;">Parameter</span>
|
||||
</th>
|
||||
<th>
|
||||
|
||||
<span style="white-space: nowrap;">Type</span>
|
||||
</th>
|
||||
<th>
|
||||
|
||||
<span style="white-space: nowrap;">Example</span>
|
||||
</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -424,7 +448,7 @@ This timeframe will be used for slicing on intervals `(fit_window, infer_window
|
||||
<td>ISO 8601</td>
|
||||
<td>
|
||||
|
||||
`from_iso`
|
||||
<span style="white-space: nowrap;">`from_iso`</span>
|
||||
</td>
|
||||
<td>str</td>
|
||||
<td>
|
||||
@@ -437,7 +461,7 @@ This timeframe will be used for slicing on intervals `(fit_window, infer_window
|
||||
<td>UNIX time</td>
|
||||
<td>
|
||||
|
||||
`from_s`
|
||||
<span style="white-space: nowrap;">`from_s`</span>
|
||||
</td>
|
||||
<td>float</td>
|
||||
<td>1648771200</td>
|
||||
@@ -446,7 +470,7 @@ This timeframe will be used for slicing on intervals `(fit_window, infer_window
|
||||
<td>ISO 8601</td>
|
||||
<td>
|
||||
|
||||
`to_iso`
|
||||
<span style="white-space: nowrap;">`to_iso`</span>
|
||||
</td>
|
||||
<td>str</td>
|
||||
<td>
|
||||
@@ -462,7 +486,7 @@ This timeframe will be used for slicing on intervals `(fit_window, infer_window
|
||||
<td>UNIX time</td>
|
||||
<td>
|
||||
|
||||
`to_s`
|
||||
<span style="white-space: nowrap;">`to_s`</span>
|
||||
</td>
|
||||
<td>float</td>
|
||||
<td>1649548800</td>
|
||||
@@ -487,7 +511,7 @@ The same *explicit* logic as in [Periodic scheduler](#periodic-scheduler)
|
||||
<td>ISO 8601</td>
|
||||
<td rowspan=2>
|
||||
|
||||
`fit_window`
|
||||
<span style="white-space: nowrap;">`fit_window`</span>
|
||||
</td>
|
||||
<td rowspan=2>str</td>
|
||||
<td>
|
||||
@@ -523,7 +547,7 @@ In `BacktestingScheduler`, the inference window is *implicitly* defined as a per
|
||||
<td>ISO 8601</td>
|
||||
<td rowspan=2>
|
||||
|
||||
`fit_every`
|
||||
<span style="white-space: nowrap;">`fit_every`</span>
|
||||
</td>
|
||||
<td rowspan=2>str</td>
|
||||
<td>
|
||||
|
||||
@@ -28,11 +28,11 @@ Future updates will introduce additional export methods, offering users more fle
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`class`
|
||||
<span style="white-space: nowrap;">`class`</span>
|
||||
</td>
|
||||
<td>
|
||||
<span>
|
||||
`writer.vm.VmWriter` or `vm`{{% available_from "v1.13.0" anomaly %}}
|
||||
|
||||
<span style="white-space: nowrap;">`writer.vm.VmWriter` or `vm`{{% available_from "v1.13.0" anomaly %}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
@@ -43,11 +43,11 @@ Name of the class needed to enable writing to VictoriaMetrics or Prometheus. VmW
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`datasource_url`
|
||||
<span style="white-space: nowrap;">`datasource_url`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`http://localhost:8481/`
|
||||
<span style="white-space: nowrap;">`http://localhost:8481/`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -57,10 +57,11 @@ Datasource URL address
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`tenant_id`
|
||||
<span style="white-space: nowrap;">`tenant_id`</span>
|
||||
</td>
|
||||
<td>
|
||||
<span>
|
||||
|
||||
`0:0`, `multitenant`{{% available_from "v1.16.2" anomaly %}}
|
||||
</span>
|
||||
</td>
|
||||
@@ -73,11 +74,11 @@ For VictoriaMetrics Cluster version only, tenants are identified by `accountID`
|
||||
<tr>
|
||||
<td rowspan="4">
|
||||
|
||||
`metric_format`
|
||||
<span style="white-space: nowrap;">`metric_format`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
`__name__: "vmanomaly_$VAR"`
|
||||
<span style="white-space: nowrap;">`__name__: "vmanomaly_$VAR"`</span>
|
||||
</td>
|
||||
<td rowspan="4">
|
||||
|
||||
@@ -99,26 +100,26 @@ Metrics to save the output (in metric names or labels). Must have `__name__` key
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`for: "$QUERY_KEY"`
|
||||
<span style="white-space: nowrap;">`for: "$QUERY_KEY"`</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`run: "test_metric_format"`
|
||||
<span style="white-space: nowrap;">`run: "test_metric_format"`</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`config: "io_vm_single.yaml"`
|
||||
<span style="white-space: nowrap;">`config: "io_vm_single.yaml"`</span>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- End of additional rows -->
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`import_json_path`
|
||||
<span style="white-space: nowrap;">`import_json_path`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -132,7 +133,7 @@ Optional, to override the default import path
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`health_path`
|
||||
<span style="white-space: nowrap;">`health_path`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -146,7 +147,7 @@ Absolute or relative URL address where to check the availability of the datasour
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`user`
|
||||
<span style="white-space: nowrap;">`user`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -160,7 +161,7 @@ BasicAuth username
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`password`
|
||||
<span style="white-space: nowrap;">`password`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -174,7 +175,7 @@ BasicAuth password
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`timeout`
|
||||
<span style="white-space: nowrap;">`timeout`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -188,7 +189,7 @@ Timeout for the requests, passed as a string
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`verify_tls`
|
||||
<span style="white-space: nowrap;">`verify_tls`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -203,7 +204,7 @@ If a path to a CA bundle file (like `ca.crt`), it will verify the certificate us
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`tls_cert_file`
|
||||
<span style="white-space: nowrap;">`tls_cert_file`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -216,7 +217,7 @@ Path to a file with the client certificate, i.e. `client.crt`{{% available_from
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`tls_key_file`
|
||||
<span style="white-space: nowrap;">`tls_key_file`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -229,7 +230,7 @@ Path to a file with the client certificate key, i.e. `client.key`{{% available_f
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`bearer_token`
|
||||
<span style="white-space: nowrap;">`bearer_token`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -242,7 +243,7 @@ Token is passed in the standard format with header: `Authorization: bearer {toke
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`bearer_token_file`
|
||||
<span style="white-space: nowrap;">`bearer_token_file`</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
- To use *vmanomaly*, part of the enterprise package, a license key is required. Obtain your key [here](https://victoriametrics.com/products/enterprise/trial/) for this tutorial or for enterprise use.
|
||||
- In the tutorial, we'll be using the following VictoriaMetrics components:
|
||||
- [VictoriaMetrics Single-Node](https://docs.victoriametrics.com/single-server-victoriametrics) (v1.108.1)
|
||||
- [vmalert](https://docs.victoriametrics.com/vmalert/) (v1.108.1)
|
||||
- [vmagent](https://docs.victoriametrics.com/vmagent/) (v1.108.1)
|
||||
- [VictoriaMetrics Single-Node](https://docs.victoriametrics.com/single-server-victoriametrics) (v1.109.0)
|
||||
- [vmalert](https://docs.victoriametrics.com/vmalert/) (v1.109.0)
|
||||
- [vmagent](https://docs.victoriametrics.com/vmagent/) (v1.109.0)
|
||||
- [Grafana](https://grafana.com/) (v.10.2.1)
|
||||
- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/)
|
||||
- [Node exporter](https://github.com/prometheus/node_exporter#node-exporter) (v1.7.0) and [Alertmanager](https://prometheus.io/docs/alerting/latest/alertmanager/) (v0.27.0)
|
||||
@@ -313,7 +313,7 @@ Let's wrap it all up together into the `docker-compose.yml` file.
|
||||
services:
|
||||
vmagent:
|
||||
container_name: vmagent
|
||||
image: victoriametrics/vmagent:v1.108.1
|
||||
image: victoriametrics/vmagent:v1.109.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
@@ -330,7 +330,7 @@ services:
|
||||
|
||||
victoriametrics:
|
||||
container_name: victoriametrics
|
||||
image: victoriametrics/victoria-metrics:v1.108.1
|
||||
image: victoriametrics/victoria-metrics:v1.109.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
volumes:
|
||||
@@ -363,7 +363,7 @@ services:
|
||||
|
||||
vmalert:
|
||||
container_name: vmalert
|
||||
image: victoriametrics/vmalert:v1.108.1
|
||||
image: victoriametrics/vmalert:v1.109.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
|
||||
@@ -18,12 +18,16 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/).
|
||||
|
||||
## tip
|
||||
|
||||
* SECURITY: upgrade base docker image (Alpine) from 3.21.0 to 3.21.2. See [Alpine 3.21.1 release notes](https://alpinelinux.org/posts/Alpine-3.21.1-released.html) and [Alpine 3.21.2 release notes](https://alpinelinux.org/posts/Alpine-3.18.11-3.19.6-3.20.5-3.21.2-released.html).
|
||||
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/), `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/) and [vmagent](https://docs.victoriametrics.com/vmagent/): log metric names for signals with unsupported delta temporality on ingestion via [OpenTelemetry protocol for metrics](https://docs.victoriametrics.com/#sending-data-via-opentelemetry). Thanks to @chenlujjj for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/8018).
|
||||
|
||||
## [v1.109.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.109.0)
|
||||
|
||||
Released at 2025-01-14
|
||||
|
||||
**Update note: This release contains [issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/8045) that might affect correctness of increase, increase_pure and delta functions. It affects only vmselect or VictoriaMetrics single-node on query time. Please, rollback vmselects to the closest previous version as temporary workaround until it is fixed.**
|
||||
|
||||
* FEATURE: all the VictoriaMetrics components: increase the default value for [`GOGC`](https://tip.golang.org/doc/gc-guide#GOGC) from `30` to `100`. This should reduce CPU usage at the cost of slightly higher memory usage. [Single-node VictoriaMetrics](https://docs.victoriametrics.com/), [vmagent](https://docs.victoriametrics.com/vmagent/) and [vmstorage](https://docs.victoriametrics.com/cluster-victoriametrics/#architecture-overview) components continue using `GOGC=30`, since they are optimized for low memory allocations and low memory usage, so they do not benefit from the increased GOGC value too much. It is possible to override the default `GOGC` value in any VictoriaMetrics component by setting `GOGC` environment variable to the desired value. For example, `GOGC=200 ./path/to/vmagent` starts `vmagent` with `GOGC=200`. See [these docs](https://tip.golang.org/doc/gc-guide#GOGC) about `GOGC` tuning. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7902).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add export data functionality for the `Raw Query` page and the ability to import exported data into the `Query Analyzer` page. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7628).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add `markdown` support for comments during data export. [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/7828).
|
||||
@@ -33,6 +37,7 @@ Released at 2025-01-14
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): improve query performance on systems with high number of CPU cores. See [this PR](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/7416) for details.
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): add command-line flag `-search.maxBinaryOpPushdownLabelValues` to allow using labels with more candidate values as push down filter in binary operation. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/7243). Thanks to @tydhot for implementation.
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): add command-line flag `storage.finalDedupScheduleCheckInterval` to control the final deduplication process interval. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7880) for details.
|
||||
* FEATURE: [Single-node VictoriaMetrics](https://docs.victoriametrics.com/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): add a separate cache type for storing sparse entries when performing large index scans. This significantly reduces memory usage when applying [downsampling filters](https://docs.victoriametrics.com/#downsampling) and [retention filters](https://docs.victoriametrics.com/#retention-filters) during background merge. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7182) for the details.
|
||||
|
||||
* BUGFIX: [dashboards](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/dashboards): consistently use `vmagent_remotewrite_pending_data_bytes` on vmagent dashboard to represent persistent queue size.
|
||||
* BUGFIX: [vmalert](https://docs.victoriametrics.com/vmalert/): fix the auto-generated metrics `ALERTS` and `ALERTS_FOR_STATE` for alerting rules. Previously, metrics might have incorrect labels and affect the restore process. See this [issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7796).
|
||||
|
||||
@@ -82,7 +82,7 @@ VictoriaMetrics Enterprise components are available in the following forms:
|
||||
It is allowed to run VictoriaMetrics Enterprise components in [cases listed here](#valid-cases-for-victoriametrics-enterprise).
|
||||
|
||||
Binary releases of VictoriaMetrics Enterprise are available [at the releases page](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest).
|
||||
Enterprise binaries and packages have `enterprise` suffix in their names. For example, `victoria-metrics-linux-amd64-v1.108.1-enterprise.tar.gz`.
|
||||
Enterprise binaries and packages have `enterprise` suffix in their names. For example, `victoria-metrics-linux-amd64-v1.109.0-enterprise.tar.gz`.
|
||||
|
||||
In order to run binary release of VictoriaMetrics Enterprise component, please download the `*-enterprise.tar.gz` archive for your OS and architecture
|
||||
from the [releases page](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest) and unpack it. Then run the unpacked binary.
|
||||
@@ -100,8 +100,8 @@ For example, the following command runs VictoriaMetrics Enterprise binary with t
|
||||
obtained at [this page](https://victoriametrics.com/products/enterprise/trial/):
|
||||
|
||||
```sh
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.108.1/victoria-metrics-linux-amd64-v1.108.1-enterprise.tar.gz
|
||||
tar -xzf victoria-metrics-linux-amd64-v1.108.1-enterprise.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.109.0/victoria-metrics-linux-amd64-v1.109.0-enterprise.tar.gz
|
||||
tar -xzf victoria-metrics-linux-amd64-v1.109.0-enterprise.tar.gz
|
||||
./victoria-metrics-prod -license=BASE64_ENCODED_LICENSE_KEY
|
||||
```
|
||||
|
||||
@@ -116,7 +116,7 @@ Alternatively, VictoriaMetrics Enterprise license can be stored in the file and
|
||||
It is allowed to run VictoriaMetrics Enterprise components in [cases listed here](#valid-cases-for-victoriametrics-enterprise).
|
||||
|
||||
Docker images for VictoriaMetrics Enterprise are available [at VictoriaMetrics DockerHub](https://hub.docker.com/u/victoriametrics).
|
||||
Enterprise docker images have `enterprise` suffix in their names. For example, `victoriametrics/victoria-metrics:v1.108.1-enterprise`.
|
||||
Enterprise docker images have `enterprise` suffix in their names. For example, `victoriametrics/victoria-metrics:v1.109.0-enterprise`.
|
||||
|
||||
In order to run Docker image of VictoriaMetrics Enterprise component, it is required to provide the license key via command-line
|
||||
flag as described [here](#binary-releases).
|
||||
@@ -126,13 +126,13 @@ Enterprise license key can be obtained at [this page](https://victoriametrics.co
|
||||
For example, the following command runs VictoriaMetrics Enterprise Docker image with the specified license key:
|
||||
|
||||
```sh
|
||||
docker run --name=victoria-metrics victoriametrics/victoria-metrics:v1.108.1-enterprise -license=BASE64_ENCODED_LICENSE_KEY
|
||||
docker run --name=victoria-metrics victoriametrics/victoria-metrics:v1.109.0-enterprise -license=BASE64_ENCODED_LICENSE_KEY
|
||||
```
|
||||
|
||||
Alternatively, the license code can be stored in the file and then referred via `-licenseFile` command-line flag:
|
||||
|
||||
```sh
|
||||
docker run --name=victoria-metrics -v /vm-license:/vm-license victoriametrics/victoria-metrics:v1.108.1-enterprise -licenseFile=/path/to/vm-license
|
||||
docker run --name=victoria-metrics -v /vm-license:/vm-license victoriametrics/victoria-metrics:v1.109.0-enterprise -licenseFile=/path/to/vm-license
|
||||
```
|
||||
|
||||
Example docker-compose configuration:
|
||||
@@ -141,7 +141,7 @@ version: "3.5"
|
||||
services:
|
||||
victoriametrics:
|
||||
container_name: victoriametrics
|
||||
image: victoriametrics/victoria-metrics:v1.108.1
|
||||
image: victoriametrics/victoria-metrics:v1.109.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
volumes:
|
||||
@@ -173,7 +173,7 @@ is used to provide key in plain-text:
|
||||
```yaml
|
||||
server:
|
||||
image:
|
||||
tag: v1.108.1-enterprise
|
||||
tag: v1.109.0-enterprise
|
||||
|
||||
license:
|
||||
key: {BASE64_ENCODED_LICENSE_KEY}
|
||||
@@ -184,7 +184,7 @@ In order to provide key via existing secret, the following values file is used:
|
||||
```yaml
|
||||
server:
|
||||
image:
|
||||
tag: v1.108.1-enterprise
|
||||
tag: v1.109.0-enterprise
|
||||
|
||||
license:
|
||||
secret:
|
||||
@@ -233,7 +233,7 @@ spec:
|
||||
license:
|
||||
key: {BASE64_ENCODED_LICENSE_KEY}
|
||||
image:
|
||||
tag: v1.108.1-enterprise
|
||||
tag: v1.109.0-enterprise
|
||||
```
|
||||
|
||||
In order to provide key via existing secret, the following custom resource is used:
|
||||
@@ -250,7 +250,7 @@ spec:
|
||||
name: vm-license
|
||||
key: license
|
||||
image:
|
||||
tag: v1.108.1-enterprise
|
||||
tag: v1.109.0-enterprise
|
||||
```
|
||||
|
||||
Example secret with license key:
|
||||
|
||||
@@ -236,27 +236,27 @@ services:
|
||||
- grafana_data:/var/lib/grafana/
|
||||
|
||||
vmsingle:
|
||||
image: victoriametrics/victoria-metrics:v1.108.1
|
||||
image: victoriametrics/victoria-metrics:v1.109.0
|
||||
command:
|
||||
- -httpListenAddr=0.0.0.0:8429
|
||||
|
||||
vmstorage:
|
||||
image: victoriametrics/vmstorage:v1.108.1-cluster
|
||||
image: victoriametrics/vmstorage:v1.109.0-cluster
|
||||
|
||||
vminsert:
|
||||
image: victoriametrics/vminsert:v1.108.1-cluster
|
||||
image: victoriametrics/vminsert:v1.109.0-cluster
|
||||
command:
|
||||
- -storageNode=vmstorage:8400
|
||||
- -httpListenAddr=0.0.0.0:8480
|
||||
|
||||
vmselect:
|
||||
image: victoriametrics/vmselect:v1.108.1-cluster
|
||||
image: victoriametrics/vmselect:v1.109.0-cluster
|
||||
command:
|
||||
- -storageNode=vmstorage:8401
|
||||
- -httpListenAddr=0.0.0.0:8481
|
||||
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.108.1
|
||||
image: victoriametrics/vmagent:v1.109.0
|
||||
volumes:
|
||||
- ./scrape.yaml:/etc/vmagent/config.yaml
|
||||
command:
|
||||
@@ -265,7 +265,7 @@ services:
|
||||
- -remoteWrite.url=http://vmsingle:8429/api/v1/write
|
||||
|
||||
vmgateway-cluster:
|
||||
image: victoriametrics/vmgateway:v1.108.1-enterprise
|
||||
image: victoriametrics/vmgateway:v1.109.0-enterprise
|
||||
ports:
|
||||
- 8431:8431
|
||||
volumes:
|
||||
@@ -281,7 +281,7 @@ services:
|
||||
- -auth.oidcDiscoveryEndpoints=http://keycloak:8080/realms/master/.well-known/openid-configuration
|
||||
|
||||
vmgateway-single:
|
||||
image: victoriametrics/vmgateway:v1.108.1-enterprise
|
||||
image: victoriametrics/vmgateway:v1.109.0-enterprise
|
||||
ports:
|
||||
- 8432:8431
|
||||
volumes:
|
||||
@@ -393,7 +393,7 @@ Once iDP configuration is done, vmagent configuration needs to be updated to use
|
||||
|
||||
```yaml
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.108.1
|
||||
image: victoriametrics/vmagent:v1.109.0
|
||||
volumes:
|
||||
- ./scrape.yaml:/etc/vmagent/config.yaml
|
||||
- ./vmagent-client-secret:/etc/vmagent/oauth2-client-secret
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.8.13
|
||||
|
||||
**Release date:** 14 Jan 2025
|
||||
|
||||
 
|
||||
|
||||
- victorialogs version: v1.4.0 -> v1.5.0
|
||||
|
||||
## 0.8.12
|
||||
|
||||
**Release date:** 06 Jan 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.15.4
|
||||
|
||||
**Release date:** 14 Jan 2025
|
||||
|
||||
 
|
||||
|
||||
- bump version of VM components to [v1.109.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.109.0)
|
||||
|
||||
## 0.15.3
|
||||
|
||||
**Release date:** 06 Jan 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.13.6
|
||||
|
||||
**Release date:** 14 Jan 2025
|
||||
|
||||
 
|
||||
|
||||
- bump version of VM components to [v1.109.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.109.0)
|
||||
|
||||
## 0.13.5
|
||||
|
||||
**Release date:** 06 Jan 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.8.4
|
||||
|
||||
**Release date:** 14 Jan 2025
|
||||
|
||||
 
|
||||
|
||||
- bump version of VM components to [v1.109.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.109.0)
|
||||
|
||||
## 0.8.3
|
||||
|
||||
**Release date:** 06 Jan 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.17.1
|
||||
|
||||
**Release date:** 14 Jan 2025
|
||||
|
||||
 
|
||||
|
||||
- bump version of VM components to [v1.109.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.109.0)
|
||||
|
||||
## 0.17.0
|
||||
|
||||
**Release date:** 09 Jan 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.7.2
|
||||
|
||||
**Release date:** 14 Jan 2025
|
||||
|
||||
 
|
||||
|
||||
- bump version of VM components to [v1.109.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.109.0)
|
||||
|
||||
## 0.7.1
|
||||
|
||||
**Release date:** 10 Jan 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.6.4
|
||||
|
||||
**Release date:** 14 Jan 2025
|
||||
|
||||
 
|
||||
|
||||
- bump version of VM components to [v1.109.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.109.0)
|
||||
|
||||
## 0.6.3
|
||||
|
||||
**Release date:** 06 Jan 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.33.4
|
||||
|
||||
**Release date:** 14 Jan 2025
|
||||
|
||||
 
|
||||
|
||||
- bump version of VM components to [v1.109.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.109.0)
|
||||
|
||||
## 0.33.3
|
||||
|
||||
**Release date:** 13 Jan 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.13.5
|
||||
|
||||
**Release date:** 14 Jan 2025
|
||||
|
||||
 
|
||||
|
||||
- bump version of VM components to [v1.109.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.109.0)
|
||||
|
||||
## 0.13.4
|
||||
|
||||
**Release date:** 06 Jan 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -30,8 +30,8 @@ scrape_configs:
|
||||
After you created the `scrape.yaml` file, download and unpack [single-node VictoriaMetrics](https://docs.victoriametrics.com/) to the same directory:
|
||||
|
||||
```
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.108.1/victoria-metrics-linux-amd64-v1.108.1.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.108.1.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.109.0/victoria-metrics-linux-amd64-v1.109.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.109.0.tar.gz
|
||||
```
|
||||
|
||||
Then start VictoriaMetrics and instruct it to scrape targets defined in `scrape.yaml` and save scraped metrics
|
||||
@@ -146,8 +146,8 @@ Then start [single-node VictoriaMetrics](https://docs.victoriametrics.com/) acco
|
||||
|
||||
```yaml
|
||||
# Download and unpack single-node VictoriaMetrics
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.108.1/victoria-metrics-linux-amd64-v1.108.1.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.108.1.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.109.0/victoria-metrics-linux-amd64-v1.109.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.109.0.tar.gz
|
||||
|
||||
# Run single-node VictoriaMetrics with the given scrape.yaml
|
||||
./victoria-metrics-prod -promscrape.config=scrape.yaml
|
||||
|
||||
@@ -292,11 +292,14 @@ func (br *blockResult) addResultColumn(rc *resultColumn) {
|
||||
logger.Panicf("BUG: column %q must contain %d rows, but it contains %d rows", rc.name, br.rowsLen, len(rc.values))
|
||||
}
|
||||
if areConstValues(rc.values) {
|
||||
// This optimization allows reducing memory usage after br cloning
|
||||
// Clone the constant value into rc, so it doesn't hold the external memory.
|
||||
// This optimization allows reducing memory usage after br cloning.
|
||||
br.addValue(rc.values[0])
|
||||
valuesEncoded := br.valuesBuf[len(br.valuesBuf)-1:]
|
||||
br.csAdd(blockResultColumn{
|
||||
name: rc.name,
|
||||
isConst: true,
|
||||
valuesEncoded: rc.values[:1],
|
||||
valuesEncoded: valuesEncoded,
|
||||
})
|
||||
} else {
|
||||
br.csAdd(blockResultColumn{
|
||||
|
||||
@@ -2,6 +2,8 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestMatchAnyCasePhrase(t *testing.T) {
|
||||
@@ -44,8 +46,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("single-row", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -119,8 +119,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "other-column",
|
||||
@@ -230,8 +228,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -287,8 +283,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -359,8 +353,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -426,8 +418,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -492,8 +482,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -558,8 +546,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -623,8 +609,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -688,8 +672,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -795,8 +777,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -893,8 +873,6 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -976,4 +954,7 @@ func TestFilterAnyCasePhrase(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, pf, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestMatchAnyCasePrefix(t *testing.T) {
|
||||
@@ -44,8 +46,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("single-row", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -137,8 +137,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "other-column",
|
||||
@@ -254,8 +252,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -311,8 +307,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -395,8 +389,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -462,8 +454,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -528,8 +518,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -594,8 +582,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -659,8 +645,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -724,8 +708,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -837,8 +819,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -935,8 +915,6 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -1018,4 +996,7 @@ func TestFilterAnyCasePrefix(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fp, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestFilterExactPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("single-row", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -59,8 +59,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -112,8 +110,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -157,8 +153,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -211,8 +205,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -266,8 +258,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -321,8 +311,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -376,8 +364,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -431,8 +417,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -498,8 +482,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -557,8 +539,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -607,8 +587,6 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -652,4 +630,7 @@ func TestFilterExactPrefix(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fep, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestFilterExact(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("single-row", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -47,8 +47,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -94,8 +92,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -139,8 +135,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -193,8 +187,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -248,8 +240,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -303,8 +293,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -358,8 +346,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -413,8 +399,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -474,8 +458,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -551,8 +533,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -613,8 +593,6 @@ func TestFilterExact(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -664,4 +642,7 @@ func TestFilterExact(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fe, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ import (
|
||||
"reflect"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestFilterIn(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("single-row", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -79,8 +79,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -132,8 +130,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -189,8 +185,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -243,8 +237,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -310,8 +302,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -377,8 +367,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -444,8 +432,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -505,8 +491,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -566,8 +550,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -649,8 +631,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -717,8 +697,6 @@ func TestFilterIn(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -774,6 +752,9 @@ func TestFilterIn(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fi, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
func TestGetCommonTokensAndTokenSets(t *testing.T) {
|
||||
|
||||
@@ -2,6 +2,8 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestMatchIPv4Range(t *testing.T) {
|
||||
@@ -33,8 +35,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -85,8 +85,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -142,8 +140,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -187,8 +183,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -218,8 +212,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -249,8 +241,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -280,8 +270,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -311,8 +299,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -342,8 +328,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -373,8 +357,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -427,8 +409,6 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -454,4 +434,7 @@ func TestFilterIPv4Range(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fr, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestMatchLenRange(t *testing.T) {
|
||||
@@ -36,8 +38,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -81,8 +81,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -123,8 +121,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -161,8 +157,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -207,8 +201,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -253,8 +245,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -299,8 +289,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -345,8 +333,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -391,8 +377,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -430,8 +414,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -470,8 +452,6 @@ func TestFilterLenRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -505,4 +485,7 @@ func TestFilterLenRange(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fr, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestMatchPhrase(t *testing.T) {
|
||||
@@ -49,8 +51,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("single-row", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -124,8 +124,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "other-column",
|
||||
@@ -235,8 +233,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -292,8 +288,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -364,8 +358,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -431,8 +423,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -497,8 +487,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -563,8 +551,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -628,8 +614,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -693,8 +677,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -800,8 +782,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -898,8 +878,6 @@ func TestFilterPhrase(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -981,4 +959,7 @@ func TestFilterPhrase(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, pf, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestMatchPrefix(t *testing.T) {
|
||||
@@ -49,8 +51,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("single-row", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -136,8 +136,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "other-column",
|
||||
@@ -253,8 +251,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -310,8 +306,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -394,8 +388,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -461,8 +453,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -527,8 +517,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -593,8 +581,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -658,8 +644,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -771,8 +755,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -869,8 +851,6 @@ func TestFilterPrefix(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -952,4 +932,7 @@ func TestFilterPrefix(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fp, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestFilterRange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -81,8 +81,6 @@ func TestFilterRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -152,8 +150,6 @@ func TestFilterRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -218,8 +214,6 @@ func TestFilterRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -286,8 +280,6 @@ func TestFilterRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -353,8 +345,6 @@ func TestFilterRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -420,8 +410,6 @@ func TestFilterRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -494,8 +482,6 @@ func TestFilterRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -568,8 +554,6 @@ func TestFilterRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -642,8 +626,6 @@ func TestFilterRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -673,8 +655,6 @@ func TestFilterRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -700,4 +680,7 @@ func TestFilterRange(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fr, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/regexutil"
|
||||
)
|
||||
|
||||
@@ -11,8 +12,6 @@ func TestFilterRegexp(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -58,8 +57,6 @@ func TestFilterRegexp(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -98,8 +95,6 @@ func TestFilterRegexp(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -134,8 +129,6 @@ func TestFilterRegexp(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -171,8 +164,6 @@ func TestFilterRegexp(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -208,8 +199,6 @@ func TestFilterRegexp(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -245,8 +234,6 @@ func TestFilterRegexp(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -282,8 +269,6 @@ func TestFilterRegexp(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -319,8 +304,6 @@ func TestFilterRegexp(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -357,8 +340,6 @@ func TestFilterRegexp(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -390,6 +371,9 @@ func TestFilterRegexp(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fr, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
func TestSkipFirstLastToken(t *testing.T) {
|
||||
|
||||
@@ -2,6 +2,8 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestMatchSequence(t *testing.T) {
|
||||
@@ -33,8 +35,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("single-row", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -108,8 +108,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -161,8 +159,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -218,8 +214,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -290,8 +284,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -363,8 +355,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -436,8 +426,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -509,8 +497,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -582,8 +568,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -661,8 +645,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -744,8 +726,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -842,8 +822,6 @@ func TestFilterSequence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -923,4 +901,7 @@ func TestFilterSequence(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fs, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestMatchStringRange(t *testing.T) {
|
||||
@@ -27,8 +29,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -86,8 +86,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -143,8 +141,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -188,8 +184,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -241,8 +235,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -294,8 +286,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -347,8 +337,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -400,8 +388,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -453,8 +439,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -506,8 +490,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -567,8 +549,6 @@ func TestFilterStringRange(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -623,4 +603,7 @@ func TestFilterStringRange(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, fr, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestFilterValueType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("single-row", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -59,8 +59,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("const-column", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "other-column",
|
||||
@@ -152,8 +150,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -185,8 +181,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -221,8 +215,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint8", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -258,8 +250,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint16", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -294,8 +284,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint32", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -330,8 +318,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uint64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -365,8 +351,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("int64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -400,8 +384,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("float64", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -435,8 +417,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "foo",
|
||||
@@ -473,8 +453,6 @@ func TestFilterValueType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("timestamp-iso8601", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := []column{
|
||||
{
|
||||
name: "_msg",
|
||||
@@ -506,4 +484,7 @@ func TestFilterValueType(t *testing.T) {
|
||||
}
|
||||
testFilterMatchForColumns(t, columns, pv, "_msg", nil)
|
||||
})
|
||||
|
||||
// Remove the remaining data files for the test
|
||||
fs.MustRemoveAll(t.Name())
|
||||
}
|
||||
|
||||
239
lib/logstorage/hits_map.go
Normal file
239
lib/logstorage/hits_map.go
Normal file
@@ -0,0 +1,239 @@
|
||||
package logstorage
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||
)
|
||||
|
||||
type hitsMap struct {
|
||||
stateSizeBudget *int
|
||||
|
||||
u64 map[uint64]*uint64
|
||||
negative64 map[uint64]*uint64
|
||||
strings map[string]*uint64
|
||||
|
||||
// a reduces memory allocations when counting the number of hits over big number of unique values.
|
||||
a chunkedAllocator
|
||||
}
|
||||
|
||||
func (hm *hitsMap) reset() {
|
||||
hm.stateSizeBudget = nil
|
||||
|
||||
hm.u64 = nil
|
||||
hm.negative64 = nil
|
||||
hm.strings = nil
|
||||
}
|
||||
|
||||
func (hm *hitsMap) clear() {
|
||||
*hm.stateSizeBudget += hm.stateSize()
|
||||
hm.init(hm.stateSizeBudget)
|
||||
}
|
||||
|
||||
func (hm *hitsMap) init(stateSizeBudget *int) {
|
||||
hm.stateSizeBudget = stateSizeBudget
|
||||
|
||||
hm.u64 = make(map[uint64]*uint64)
|
||||
hm.negative64 = make(map[uint64]*uint64)
|
||||
hm.strings = make(map[string]*uint64)
|
||||
}
|
||||
|
||||
func (hm *hitsMap) entriesCount() uint64 {
|
||||
n := len(hm.u64) + len(hm.negative64) + len(hm.strings)
|
||||
return uint64(n)
|
||||
}
|
||||
|
||||
func (hm *hitsMap) stateSize() int {
|
||||
n := 24*(len(hm.u64)+len(hm.negative64)) + 40*len(hm.strings)
|
||||
for k := range hm.strings {
|
||||
n += len(k)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (hm *hitsMap) updateStateGeneric(key string, hits uint64) {
|
||||
if n, ok := tryParseUint64(key); ok {
|
||||
hm.updateStateUint64(n, hits)
|
||||
return
|
||||
}
|
||||
if len(key) > 0 && key[0] == '-' {
|
||||
if n, ok := tryParseInt64(key); ok {
|
||||
hm.updateStateNegativeInt64(n, hits)
|
||||
return
|
||||
}
|
||||
}
|
||||
hm.updateStateString(bytesutil.ToUnsafeBytes(key), hits)
|
||||
}
|
||||
|
||||
func (hm *hitsMap) updateStateInt64(n int64, hits uint64) {
|
||||
if n >= 0 {
|
||||
hm.updateStateUint64(uint64(n), hits)
|
||||
} else {
|
||||
hm.updateStateNegativeInt64(n, hits)
|
||||
}
|
||||
}
|
||||
|
||||
func (hm *hitsMap) updateStateUint64(n, hits uint64) {
|
||||
pHits := hm.u64[n]
|
||||
if pHits != nil {
|
||||
*pHits += hits
|
||||
return
|
||||
}
|
||||
|
||||
pHits = hm.a.newUint64()
|
||||
*pHits = hits
|
||||
hm.u64[n] = pHits
|
||||
|
||||
*hm.stateSizeBudget -= 24
|
||||
}
|
||||
|
||||
func (hm *hitsMap) updateStateNegativeInt64(n int64, hits uint64) {
|
||||
pHits := hm.negative64[uint64(n)]
|
||||
if pHits != nil {
|
||||
*pHits += hits
|
||||
return
|
||||
}
|
||||
|
||||
pHits = hm.a.newUint64()
|
||||
*pHits = hits
|
||||
hm.negative64[uint64(n)] = pHits
|
||||
|
||||
*hm.stateSizeBudget -= 24
|
||||
}
|
||||
|
||||
func (hm *hitsMap) updateStateString(key []byte, hits uint64) {
|
||||
pHits := hm.strings[string(key)]
|
||||
if pHits != nil {
|
||||
*pHits += hits
|
||||
return
|
||||
}
|
||||
|
||||
keyCopy := hm.a.cloneBytesToString(key)
|
||||
pHits = hm.a.newUint64()
|
||||
*pHits = hits
|
||||
hm.strings[keyCopy] = pHits
|
||||
|
||||
*hm.stateSizeBudget -= len(keyCopy) + 40
|
||||
}
|
||||
|
||||
func (hm *hitsMap) mergeState(src *hitsMap, stopCh <-chan struct{}) {
|
||||
for n, pHitsSrc := range src.u64 {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
}
|
||||
pHitsDst := hm.u64[n]
|
||||
if pHitsDst == nil {
|
||||
hm.u64[n] = pHitsSrc
|
||||
} else {
|
||||
*pHitsDst += *pHitsSrc
|
||||
}
|
||||
}
|
||||
for n, pHitsSrc := range src.negative64 {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
}
|
||||
pHitsDst := hm.negative64[n]
|
||||
if pHitsDst == nil {
|
||||
hm.negative64[n] = pHitsSrc
|
||||
} else {
|
||||
*pHitsDst += *pHitsSrc
|
||||
}
|
||||
}
|
||||
for k, pHitsSrc := range src.strings {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
}
|
||||
pHitsDst := hm.strings[k]
|
||||
if pHitsDst == nil {
|
||||
hm.strings[k] = pHitsSrc
|
||||
} else {
|
||||
*pHitsDst += *pHitsSrc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hitsMapMergeParallel merges hms in parallel on the given cpusCount
|
||||
//
|
||||
// The mered disjoint parts of hms are passed to f.
|
||||
// The function may be interrupted by closing stopCh.
|
||||
// The caller must check for closed stopCh after returning from the function.
|
||||
func hitsMapMergeParallel(hms []*hitsMap, cpusCount int, stopCh <-chan struct{}, f func(hm *hitsMap)) {
|
||||
srcLen := len(hms)
|
||||
if srcLen < 2 {
|
||||
// Nothing to merge
|
||||
if len(hms) == 1 {
|
||||
f(hms[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
perShardMaps := make([][]hitsMap, srcLen)
|
||||
for i := range hms {
|
||||
wg.Add(1)
|
||||
go func(idx int) {
|
||||
defer wg.Done()
|
||||
|
||||
stateSizeBudget := 0
|
||||
perCPU := make([]hitsMap, cpusCount)
|
||||
for i := range perCPU {
|
||||
perCPU[i].init(&stateSizeBudget)
|
||||
}
|
||||
|
||||
hm := hms[idx]
|
||||
|
||||
for n, pHits := range hm.u64 {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
}
|
||||
k := unsafe.Slice((*byte)(unsafe.Pointer(&n)), 8)
|
||||
h := xxhash.Sum64(k)
|
||||
cpuIdx := h % uint64(len(perCPU))
|
||||
perCPU[cpuIdx].u64[n] = pHits
|
||||
}
|
||||
for n, pHits := range hm.negative64 {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
}
|
||||
k := unsafe.Slice((*byte)(unsafe.Pointer(&n)), 8)
|
||||
h := xxhash.Sum64(k)
|
||||
cpuIdx := h % uint64(len(perCPU))
|
||||
perCPU[cpuIdx].negative64[n] = pHits
|
||||
}
|
||||
for k, pHits := range hm.strings {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
}
|
||||
h := xxhash.Sum64(bytesutil.ToUnsafeBytes(k))
|
||||
cpuIdx := h % uint64(len(perCPU))
|
||||
perCPU[cpuIdx].strings[k] = pHits
|
||||
}
|
||||
|
||||
perShardMaps[idx] = perCPU
|
||||
hm.reset()
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
}
|
||||
|
||||
// Merge per-shard entries into perShardMaps[0]
|
||||
for i := 0; i < cpusCount; i++ {
|
||||
wg.Add(1)
|
||||
go func(cpuIdx int) {
|
||||
defer wg.Done()
|
||||
|
||||
hm := &perShardMaps[0][cpuIdx]
|
||||
for _, perCPU := range perShardMaps[1:] {
|
||||
hm.mergeState(&perCPU[cpuIdx], stopCh)
|
||||
perCPU[cpuIdx].reset()
|
||||
}
|
||||
f(hm)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
@@ -396,6 +396,7 @@ func (q *Query) CanReturnLastNResults() bool {
|
||||
*pipeTop,
|
||||
*pipeSort,
|
||||
*pipeStats,
|
||||
*pipeUnion,
|
||||
*pipeUniq:
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1322,6 +1322,10 @@ func TestParseQuerySuccess(t *testing.T) {
|
||||
f("* | extract foo<bar>baz from x", `* | extract "foo<bar>baz" from x`)
|
||||
f("* | extract if (a:b) foo<bar>baz from x", `* | extract if (a:b) "foo<bar>baz" from x`)
|
||||
|
||||
// union pipe
|
||||
f(`* | union(foo)`, `* | union (foo)`)
|
||||
f(`* | union(foo | union(bar baz | count() x))`, `* | union (foo | union (bar baz | stats count(*) as x))`)
|
||||
|
||||
// unpack_json pipe
|
||||
f(`* | unpack_json`, `* | unpack_json`)
|
||||
f(`* | unpack_json result_prefix y`, `* | unpack_json result_prefix y`)
|
||||
@@ -1838,6 +1842,12 @@ func TestParseQueryFailure(t *testing.T) {
|
||||
f(`foo | extract from x "<abc`)
|
||||
f(`foo | extract from x "<abc>" de`)
|
||||
|
||||
// invalid union pipe
|
||||
f(`foo | union`)
|
||||
f(`foo | union (`)
|
||||
f(`foo | union ( bar`)
|
||||
f(`foo | union (bar | count)`)
|
||||
|
||||
// invalid unpack_json pipe
|
||||
f(`foo | unpack_json bar`)
|
||||
f(`foo | unpack_json from`)
|
||||
@@ -2195,6 +2205,7 @@ func TestQueryGetNeededColumns(t *testing.T) {
|
||||
f(`* | stats count_uniq(a, b) if (q:w p:a) as c | count() r1`, ``, ``)
|
||||
f(`* | stats by (a1,a2) count_uniq(a, b) as c | count() r1`, `a1,a2`, ``)
|
||||
f(`* | stats by (a1,a2) count_uniq(a, b) if (q:w p:a) as c | count() r1`, `a1,a2`, ``)
|
||||
f(`* | union (foo) | count() r1`, ``, ``)
|
||||
f(`* | uniq by (a, b) | count() r1`, `a,b`, ``)
|
||||
f(`* | unpack_json from x | count() r1`, ``, ``)
|
||||
f(`* | unpack_json from x fields (a,b) | count() r1`, ``, ``)
|
||||
@@ -2277,6 +2288,7 @@ func TestQueryCanReturnLastNResults(t *testing.T) {
|
||||
f("* | len(x)", true)
|
||||
f("* | limit 10", false)
|
||||
f("* | offset 10", false)
|
||||
f("* | union (x)", false)
|
||||
f("* | uniq (x)", false)
|
||||
f("* | block_stats", false)
|
||||
f("* | blocks_count", false)
|
||||
@@ -2332,6 +2344,7 @@ func TestQueryCanLiveTail(t *testing.T) {
|
||||
f("* | stats count() rows", false)
|
||||
f("* | stream_context after 10", false)
|
||||
f("* | top 10 by (x)", false)
|
||||
f("* | union (foo)", false)
|
||||
f("* | uniq by (a)", false)
|
||||
f("* | unpack_json", true)
|
||||
f("* | unpack_logfmt", true)
|
||||
@@ -2538,6 +2551,7 @@ func TestQueryGetStatsByFields_Failure(t *testing.T) {
|
||||
f(`foo | count() | replace_regexp ("foo.+bar", "baz")`)
|
||||
f(`foo | count() | stream_context after 10`)
|
||||
f(`foo | count() | top 5 by (x)`)
|
||||
f(`foo | count() | union (foo)`)
|
||||
f(`foo | count() | uniq by (x)`)
|
||||
f(`foo | count() | unpack_json`)
|
||||
f(`foo | count() | unpack_logfmt`)
|
||||
|
||||
@@ -276,6 +276,12 @@ func parsePipe(lex *lexer) (pipe, error) {
|
||||
return nil, fmt.Errorf("cannot parse 'top' pipe: %w", err)
|
||||
}
|
||||
return pt, nil
|
||||
case lex.isKeyword("union"):
|
||||
pu, err := parsePipeUnion(lex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse 'union' pipe: %w", err)
|
||||
}
|
||||
return pu, nil
|
||||
case lex.isKeyword("uniq"):
|
||||
pu, err := parsePipeUniq(lex)
|
||||
if err != nil {
|
||||
@@ -359,6 +365,7 @@ var pipeNames = func() map[string]struct{} {
|
||||
"stats", "by",
|
||||
"stream_context",
|
||||
"top",
|
||||
"union",
|
||||
"uniq",
|
||||
"unpack_json",
|
||||
"unpack_logfmt",
|
||||
|
||||
@@ -136,14 +136,12 @@ type pipeFacetsProcessorShardNopad struct {
|
||||
}
|
||||
|
||||
type pipeFacetsFieldHits struct {
|
||||
m map[string]*uint64
|
||||
m hitsMap
|
||||
mustIgnore bool
|
||||
}
|
||||
|
||||
func (fhs *pipeFacetsFieldHits) enableIgnoreField(shard *pipeFacetsProcessorShard) {
|
||||
mLen := len(fhs.m)
|
||||
fhs.m = nil
|
||||
shard.stateSizeBudget += mLen * 8
|
||||
func (fhs *pipeFacetsFieldHits) enableIgnoreField() {
|
||||
fhs.m.clear()
|
||||
fhs.mustIgnore = true
|
||||
}
|
||||
|
||||
@@ -153,7 +151,6 @@ func (shard *pipeFacetsProcessorShard) writeBlock(br *blockResult) {
|
||||
for _, c := range cs {
|
||||
shard.updateFacetsForColumn(br, c)
|
||||
}
|
||||
|
||||
shard.rowsTotal += uint64(br.rowsLen)
|
||||
}
|
||||
|
||||
@@ -162,28 +159,124 @@ func (shard *pipeFacetsProcessorShard) updateFacetsForColumn(br *blockResult, c
|
||||
if fhs.mustIgnore {
|
||||
return
|
||||
}
|
||||
if c.isConst {
|
||||
v := c.valuesEncoded[0]
|
||||
shard.updateState(fhs, v, uint64(br.rowsLen))
|
||||
return
|
||||
}
|
||||
if c.valueType == valueTypeDict {
|
||||
c.forEachDictValueWithHits(br, func(v string, hits uint64) {
|
||||
shard.updateState(fhs, v, hits)
|
||||
})
|
||||
if fhs.m.entriesCount() >= shard.pf.maxValuesPerField {
|
||||
// Ignore fields with too many unique values
|
||||
fhs.enableIgnoreField()
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < br.rowsLen; i++ {
|
||||
v := c.getValueAtRow(br, i)
|
||||
shard.updateState(fhs, v, 1)
|
||||
if c.isConst {
|
||||
v := c.valuesEncoded[0]
|
||||
shard.updateStateGeneric(fhs, v, uint64(br.rowsLen))
|
||||
return
|
||||
}
|
||||
|
||||
switch c.valueType {
|
||||
case valueTypeDict:
|
||||
c.forEachDictValueWithHits(br, func(v string, hits uint64) {
|
||||
shard.updateStateGeneric(fhs, v, hits)
|
||||
})
|
||||
case valueTypeUint8:
|
||||
values := c.getValuesEncoded(br)
|
||||
for _, v := range values {
|
||||
n := unmarshalUint8(v)
|
||||
shard.updateStateUint64(fhs, uint64(n))
|
||||
}
|
||||
case valueTypeUint16:
|
||||
values := c.getValuesEncoded(br)
|
||||
for _, v := range values {
|
||||
n := unmarshalUint16(v)
|
||||
shard.updateStateUint64(fhs, uint64(n))
|
||||
}
|
||||
case valueTypeUint32:
|
||||
values := c.getValuesEncoded(br)
|
||||
for _, v := range values {
|
||||
n := unmarshalUint32(v)
|
||||
shard.updateStateUint64(fhs, uint64(n))
|
||||
}
|
||||
case valueTypeUint64:
|
||||
values := c.getValuesEncoded(br)
|
||||
for _, v := range values {
|
||||
n := unmarshalUint64(v)
|
||||
shard.updateStateUint64(fhs, n)
|
||||
}
|
||||
case valueTypeInt64:
|
||||
values := c.getValuesEncoded(br)
|
||||
for _, v := range values {
|
||||
n := unmarshalInt64(v)
|
||||
shard.updateStateInt64(fhs, n)
|
||||
}
|
||||
default:
|
||||
for i := 0; i < br.rowsLen; i++ {
|
||||
v := c.getValueAtRow(br, i)
|
||||
shard.updateStateGeneric(fhs, v, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (shard *pipeFacetsProcessorShard) updateState(fhs *pipeFacetsFieldHits, v string, hits uint64) {
|
||||
if fhs.mustIgnore {
|
||||
func (shard *pipeFacetsProcessorShard) updateStateInt64(fhs *pipeFacetsFieldHits, n int64) {
|
||||
if maxValueLen := shard.pf.maxValueLen; maxValueLen <= 21 && uint64(int64StringLen(n)) > maxValueLen {
|
||||
// Ignore fields with too long values, since they are hard to use in faceted search.
|
||||
fhs.enableIgnoreField()
|
||||
return
|
||||
}
|
||||
fhs.m.updateStateInt64(n, 1)
|
||||
}
|
||||
|
||||
func (shard *pipeFacetsProcessorShard) updateStateUint64(fhs *pipeFacetsFieldHits, n uint64) {
|
||||
if maxValueLen := shard.pf.maxValueLen; maxValueLen <= 20 && uint64(uint64StringLen(n)) > maxValueLen {
|
||||
// Ignore fields with too long values, since they are hard to use in faceted search.
|
||||
fhs.enableIgnoreField()
|
||||
return
|
||||
}
|
||||
fhs.m.updateStateUint64(n, 1)
|
||||
}
|
||||
|
||||
func int64StringLen(n int64) int {
|
||||
if n >= 0 {
|
||||
return uint64StringLen(uint64(n))
|
||||
}
|
||||
if n == -1<<63 {
|
||||
return 21
|
||||
}
|
||||
return 1 + uint64StringLen(uint64(-n))
|
||||
}
|
||||
|
||||
func uint64StringLen(n uint64) int {
|
||||
if n < 10 {
|
||||
return 1
|
||||
}
|
||||
if n < 100 {
|
||||
return 2
|
||||
}
|
||||
if n < 1_000 {
|
||||
return 3
|
||||
}
|
||||
if n < 10_000 {
|
||||
return 4
|
||||
}
|
||||
if n < 100_000 {
|
||||
return 5
|
||||
}
|
||||
if n < 1_000_000 {
|
||||
return 6
|
||||
}
|
||||
if n < 10_000_000 {
|
||||
return 7
|
||||
}
|
||||
if n < 100_000_000 {
|
||||
return 8
|
||||
}
|
||||
if n < 1_000_000_000 {
|
||||
return 9
|
||||
}
|
||||
if n < 10_000_000_000 {
|
||||
return 10
|
||||
}
|
||||
return 20
|
||||
}
|
||||
|
||||
func (shard *pipeFacetsProcessorShard) updateStateGeneric(fhs *pipeFacetsFieldHits, v string, hits uint64) {
|
||||
if len(v) == 0 {
|
||||
// It is impossible to calculate properly the number of hits
|
||||
// for all empty per-field values - the final number will be misleading,
|
||||
@@ -193,23 +286,10 @@ func (shard *pipeFacetsProcessorShard) updateState(fhs *pipeFacetsFieldHits, v s
|
||||
}
|
||||
if uint64(len(v)) > shard.pf.maxValueLen {
|
||||
// Ignore fields with too long values, since they are hard to use in faceted search.
|
||||
fhs.enableIgnoreField(shard)
|
||||
fhs.enableIgnoreField()
|
||||
return
|
||||
}
|
||||
|
||||
pHits := fhs.m[v]
|
||||
if pHits == nil {
|
||||
if uint64(len(fhs.m)) >= shard.pf.maxValuesPerField {
|
||||
// Ignore fields with too many unique values
|
||||
fhs.enableIgnoreField(shard)
|
||||
return
|
||||
}
|
||||
vCopy := shard.a.cloneString(v)
|
||||
pHits = shard.a.newUint64()
|
||||
fhs.m[vCopy] = pHits
|
||||
shard.stateSizeBudget -= len(vCopy) + int(unsafe.Sizeof(vCopy)+unsafe.Sizeof(hits)+unsafe.Sizeof(pHits))
|
||||
}
|
||||
*pHits += hits
|
||||
fhs.m.updateStateGeneric(v, hits)
|
||||
}
|
||||
|
||||
func (shard *pipeFacetsProcessorShard) getFieldHits(fieldName string) *pipeFacetsFieldHits {
|
||||
@@ -218,12 +298,11 @@ func (shard *pipeFacetsProcessorShard) getFieldHits(fieldName string) *pipeFacet
|
||||
}
|
||||
fhs, ok := shard.m[fieldName]
|
||||
if !ok {
|
||||
fhs = &pipeFacetsFieldHits{
|
||||
m: make(map[string]*uint64),
|
||||
}
|
||||
fhs = &pipeFacetsFieldHits{}
|
||||
fhs.m.init(&shard.stateSizeBudget)
|
||||
fieldNameCopy := shard.a.cloneString(fieldName)
|
||||
shard.m[fieldNameCopy] = fhs
|
||||
shard.stateSizeBudget -= len(fieldNameCopy) + int(unsafe.Sizeof(*fhs))
|
||||
shard.stateSizeBudget -= len(fieldNameCopy) + int(unsafe.Sizeof(fhs)+unsafe.Sizeof(*fhs))
|
||||
}
|
||||
return fhs
|
||||
}
|
||||
@@ -258,7 +337,7 @@ func (pfp *pipeFacetsProcessor) flush() error {
|
||||
}
|
||||
|
||||
// merge state across shards
|
||||
m := make(map[string]map[string]*uint64)
|
||||
hms := make(map[string]*hitsMap)
|
||||
rowsTotal := uint64(0)
|
||||
for _, shard := range pfp.shards {
|
||||
if needStop(pfp.stopCh) {
|
||||
@@ -268,26 +347,19 @@ func (pfp *pipeFacetsProcessor) flush() error {
|
||||
if fhs.mustIgnore {
|
||||
continue
|
||||
}
|
||||
vs, ok := m[fieldName]
|
||||
hm, ok := hms[fieldName]
|
||||
if !ok {
|
||||
m[fieldName] = fhs.m
|
||||
hms[fieldName] = &fhs.m
|
||||
continue
|
||||
}
|
||||
for v, pHits := range fhs.m {
|
||||
ph, ok := vs[v]
|
||||
if !ok {
|
||||
vs[v] = pHits
|
||||
} else {
|
||||
*ph += *pHits
|
||||
}
|
||||
}
|
||||
hm.mergeState(&fhs.m, pfp.stopCh)
|
||||
}
|
||||
rowsTotal += shard.rowsTotal
|
||||
}
|
||||
|
||||
// sort fieldNames
|
||||
fieldNames := make([]string, 0, len(m))
|
||||
for fieldName := range m {
|
||||
fieldNames := make([]string, 0, len(hms))
|
||||
for fieldName := range hms {
|
||||
fieldNames = append(fieldNames, fieldName)
|
||||
}
|
||||
sort.Strings(fieldNames)
|
||||
@@ -301,13 +373,25 @@ func (pfp *pipeFacetsProcessor) flush() error {
|
||||
if needStop(pfp.stopCh) {
|
||||
return nil
|
||||
}
|
||||
values := m[fieldName]
|
||||
if uint64(len(values)) > pfp.pf.maxValuesPerField {
|
||||
hm := hms[fieldName]
|
||||
if hm.entriesCount() > pfp.pf.maxValuesPerField {
|
||||
continue
|
||||
}
|
||||
|
||||
vs := make([]pipeTopEntry, 0, len(values))
|
||||
for k, pHits := range values {
|
||||
vs := make([]pipeTopEntry, 0, hm.entriesCount())
|
||||
for n, pHits := range hm.u64 {
|
||||
vs = append(vs, pipeTopEntry{
|
||||
k: string(marshalUint64String(nil, n)),
|
||||
hits: *pHits,
|
||||
})
|
||||
}
|
||||
for n, pHits := range hm.negative64 {
|
||||
vs = append(vs, pipeTopEntry{
|
||||
k: string(marshalInt64String(nil, int64(n))),
|
||||
hits: *pHits,
|
||||
})
|
||||
}
|
||||
for k, pHits := range hm.strings {
|
||||
vs = append(vs, pipeTopEntry{
|
||||
k: k,
|
||||
hits: *pHits,
|
||||
@@ -365,7 +449,9 @@ func (wctx *pipeFacetsWriteContext) writeRow(fieldName, fieldValue string, hits
|
||||
wctx.valuesLen += len(hitsStr)
|
||||
|
||||
wctx.rowsCount++
|
||||
if wctx.valuesLen >= 1_000_000 {
|
||||
|
||||
// The 64_000 limit provides the best performance results.
|
||||
if wctx.valuesLen >= 64_000 {
|
||||
wctx.flush()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ func TestParsePipeJoinSuccess(t *testing.T) {
|
||||
f(`join by (foo) (error)`)
|
||||
f(`join by (foo, bar) (a:b | fields x, y)`)
|
||||
f(`join by (foo) (a:b) prefix c`)
|
||||
f(`join by (foo) (bar | join by (x, z) (y))`)
|
||||
}
|
||||
|
||||
func TestParsePipeJoinFailure(t *testing.T) {
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/valyala/fastrand"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/decimal"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
||||
@@ -275,20 +277,18 @@ func (shard *pipeMathProcessorShard) executeMathEntry(e *mathEntry, rc *resultCo
|
||||
|
||||
shard.executeExpr(e.expr, br)
|
||||
r := shard.rs[0]
|
||||
if len(r) == 0 {
|
||||
return nan, nan
|
||||
}
|
||||
|
||||
b := shard.a.b
|
||||
minValue := nan
|
||||
maxValue := nan
|
||||
minValue := r[0]
|
||||
maxValue := r[0]
|
||||
for _, f := range r {
|
||||
if math.IsNaN(minValue) {
|
||||
if f < minValue {
|
||||
minValue = f
|
||||
} else if f > maxValue {
|
||||
maxValue = f
|
||||
} else if !math.IsNaN(f) {
|
||||
if f < minValue {
|
||||
minValue = f
|
||||
} else if f > maxValue {
|
||||
maxValue = f
|
||||
}
|
||||
}
|
||||
n := math.Float64bits(f)
|
||||
bLen := len(b)
|
||||
@@ -318,41 +318,7 @@ func (shard *pipeMathProcessorShard) executeExpr(me *mathExpr, br *blockResult)
|
||||
if me.fieldName != "" {
|
||||
r := shard.rs[rIdx]
|
||||
c := br.getColumnByName(me.fieldName)
|
||||
switch c.valueType {
|
||||
case valueTypeUint8:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
r[i] = float64(unmarshalUint8(v))
|
||||
}
|
||||
case valueTypeUint16:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
r[i] = float64(unmarshalUint16(v))
|
||||
}
|
||||
case valueTypeUint32:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
r[i] = float64(unmarshalUint32(v))
|
||||
}
|
||||
case valueTypeUint64:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
r[i] = float64(unmarshalUint64(v))
|
||||
}
|
||||
case valueTypeInt64:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
r[i] = float64(unmarshalInt64(v))
|
||||
}
|
||||
case valueTypeFloat64:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
r[i] = unmarshalFloat64(v)
|
||||
}
|
||||
default:
|
||||
values := c.getValues(br)
|
||||
var f float64
|
||||
for i, v := range values {
|
||||
if i == 0 || v != values[i-1] {
|
||||
f = parseMathNumber(v)
|
||||
}
|
||||
r[i] = f
|
||||
}
|
||||
}
|
||||
shard.loadArgValuesFromColumn(r, br, c)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -369,6 +335,80 @@ func (shard *pipeMathProcessorShard) executeExpr(me *mathExpr, br *blockResult)
|
||||
shard.rsBuf = shard.rsBuf[:rsBufLen]
|
||||
}
|
||||
|
||||
func (shard *pipeMathProcessorShard) loadArgValuesFromColumn(dst []float64, br *blockResult, c *blockResultColumn) {
|
||||
if c.isConst {
|
||||
v := c.valuesEncoded[0]
|
||||
f := parseMathNumber(v)
|
||||
for i := range dst {
|
||||
dst[i] = f
|
||||
}
|
||||
return
|
||||
}
|
||||
if c.isTime {
|
||||
timestamps := br.getTimestamps()
|
||||
for i, ts := range timestamps {
|
||||
dst[i] = float64(ts)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch c.valueType {
|
||||
case valueTypeDict:
|
||||
a := encoding.GetFloat64s(len(c.dictValues))
|
||||
fs := a.A
|
||||
for i, v := range c.dictValues {
|
||||
fs[i] = parseMathNumber(v)
|
||||
}
|
||||
values := c.getValuesEncoded(br)
|
||||
for i, v := range values {
|
||||
idx := v[0]
|
||||
dst[i] = fs[idx]
|
||||
}
|
||||
encoding.PutFloat64s(a)
|
||||
case valueTypeUint8:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
dst[i] = float64(unmarshalUint8(v))
|
||||
}
|
||||
case valueTypeUint16:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
dst[i] = float64(unmarshalUint16(v))
|
||||
}
|
||||
case valueTypeUint32:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
dst[i] = float64(unmarshalUint32(v))
|
||||
}
|
||||
case valueTypeUint64:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
dst[i] = float64(unmarshalUint64(v))
|
||||
}
|
||||
case valueTypeInt64:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
dst[i] = float64(unmarshalInt64(v))
|
||||
}
|
||||
case valueTypeFloat64:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
dst[i] = unmarshalFloat64(v)
|
||||
}
|
||||
case valueTypeIPv4:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
dst[i] = float64(unmarshalIPv4(v))
|
||||
}
|
||||
case valueTypeTimestampISO8601:
|
||||
for i, v := range c.getValuesEncoded(br) {
|
||||
dst[i] = float64(unmarshalTimestampISO8601(v))
|
||||
}
|
||||
default:
|
||||
values := c.getValues(br)
|
||||
var f float64
|
||||
for i, v := range values {
|
||||
if i == 0 || v != values[i-1] {
|
||||
f = parseMathNumber(v)
|
||||
}
|
||||
dst[i] = f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pmp *pipeMathProcessor) writeBlock(workerID uint, br *blockResult) {
|
||||
if br.rowsLen == 0 {
|
||||
return
|
||||
@@ -538,6 +578,8 @@ func parseMathExprOperand(lex *lexer) (*mathExpr, error) {
|
||||
return parseMathExprMax(lex)
|
||||
case lex.isKeyword("min"):
|
||||
return parseMathExprMin(lex)
|
||||
case lex.isKeyword("rand"):
|
||||
return parseMathExprRand(lex)
|
||||
case lex.isKeyword("round"):
|
||||
return parseMathExprRound(lex)
|
||||
case lex.isKeyword("ceil"):
|
||||
@@ -612,6 +654,26 @@ func parseMathExprMin(lex *lexer) (*mathExpr, error) {
|
||||
return me, nil
|
||||
}
|
||||
|
||||
func parseMathExprRand(lex *lexer) (*mathExpr, error) {
|
||||
if !lex.isKeyword("rand") {
|
||||
return nil, fmt.Errorf("missing 'rand' keyword")
|
||||
}
|
||||
lex.nextToken()
|
||||
|
||||
args, err := parseMathFuncArgs(lex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse args for 'rand' function: %w", err)
|
||||
}
|
||||
if len(args) != 0 {
|
||||
return nil, fmt.Errorf("'rand' function must have no args; got %d args", len(args))
|
||||
}
|
||||
me := &mathExpr{
|
||||
op: "rand",
|
||||
f: mathFuncRand,
|
||||
}
|
||||
return me, nil
|
||||
}
|
||||
|
||||
func parseMathExprRound(lex *lexer) (*mathExpr, error) {
|
||||
me, err := parseMathExprGenericFunc(lex, "round", mathFuncRound)
|
||||
if err != nil {
|
||||
@@ -927,6 +989,13 @@ func mathFuncFloor(result []float64, args [][]float64) {
|
||||
}
|
||||
}
|
||||
|
||||
func mathFuncRand(result []float64, _ [][]float64) {
|
||||
for i := range result {
|
||||
n := fastrand.Uint32()
|
||||
result[i] = float64(n) / (1 << 32)
|
||||
}
|
||||
}
|
||||
|
||||
func mathFuncRound(result []float64, args [][]float64) {
|
||||
arg := args[0]
|
||||
if len(args) == 1 {
|
||||
|
||||
@@ -21,6 +21,7 @@ func TestParsePipeMathSuccess(t *testing.T) {
|
||||
f(`math (foo + bar / baz - abc) as a`)
|
||||
f(`math min(3, foo, (1 + bar) / baz) as a, max(a, b) as b, (abs(c) + 5) as d`)
|
||||
f(`math round(foo) as x`)
|
||||
f(`math rand() as y`)
|
||||
f(`math round(foo, 0.1) as y`)
|
||||
f(`math (a / b default 10) as z`)
|
||||
f(`math (ln(a) + exp(b)) as x`)
|
||||
@@ -47,6 +48,7 @@ func TestParsePipeMathFailure(t *testing.T) {
|
||||
f(`math max(a) as x`)
|
||||
f(`math round() as x`)
|
||||
f(`math round(a, b, c) as x`)
|
||||
f(`math rand(123) as x`)
|
||||
}
|
||||
|
||||
func TestPipeMath(t *testing.T) {
|
||||
|
||||
@@ -203,7 +203,7 @@ func (ps *pipeStats) initRateFuncs(step int64) {
|
||||
const stateSizeBudgetChunk = 1 << 20
|
||||
|
||||
func (ps *pipeStats) newPipeProcessor(workersCount int, stopCh <-chan struct{}, cancel func(), ppNext pipeProcessor) pipeProcessor {
|
||||
maxStateSize := int64(float64(memory.Allowed()) * 0.3)
|
||||
maxStateSize := int64(float64(memory.Allowed()) * 0.4)
|
||||
|
||||
shards := make([]pipeStatsProcessorShard, workersCount)
|
||||
for i := range shards {
|
||||
@@ -326,7 +326,7 @@ func (psm *pipeStatsGroupMap) getPipeStatsGroupUint64(n uint64) *pipeStatsGroup
|
||||
|
||||
psg = psm.newPipeStatsGroup()
|
||||
psm.u64[n] = psg
|
||||
psm.shard.stateSizeBudget -= 8
|
||||
psm.shard.stateSizeBudget -= int(unsafe.Sizeof(n) + unsafe.Sizeof(psg))
|
||||
|
||||
return psg
|
||||
}
|
||||
@@ -339,7 +339,7 @@ func (psm *pipeStatsGroupMap) getPipeStatsGroupNegativeInt64(n int64) *pipeStats
|
||||
|
||||
psg = psm.newPipeStatsGroup()
|
||||
psm.negative64[uint64(n)] = psg
|
||||
psm.shard.stateSizeBudget -= 8
|
||||
psm.shard.stateSizeBudget -= int(unsafe.Sizeof(n) + unsafe.Sizeof(psg))
|
||||
|
||||
return psg
|
||||
}
|
||||
@@ -370,7 +370,7 @@ func (psm *pipeStatsGroupMap) newPipeStatsGroup() *pipeStatsGroup {
|
||||
psg := psm.a.newPipeStatsGroup()
|
||||
psg.funcs = psm.shard.ps.funcs
|
||||
psg.sfps = sfps
|
||||
psm.shard.stateSizeBudget -= int(unsafe.Sizeof(psg) + unsafe.Sizeof(sfps[0])*uintptr(len(sfps)))
|
||||
psm.shard.stateSizeBudget -= int(unsafe.Sizeof(*psg) + unsafe.Sizeof(sfps[0])*uintptr(len(sfps)))
|
||||
|
||||
return psg
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
||||
@@ -84,7 +82,7 @@ func (pt *pipeTop) initFilterInValues(_ *inValuesCache, _ getFieldValuesFunc) (p
|
||||
}
|
||||
|
||||
func (pt *pipeTop) newPipeProcessor(workersCount int, stopCh <-chan struct{}, cancel func(), ppNext pipeProcessor) pipeProcessor {
|
||||
maxStateSize := int64(float64(memory.Allowed()) * 0.2)
|
||||
maxStateSize := int64(float64(memory.Allowed()) * 0.4)
|
||||
|
||||
shards := make([]pipeTopProcessorShard, workersCount)
|
||||
for i := range shards {
|
||||
@@ -93,7 +91,7 @@ func (pt *pipeTop) newPipeProcessor(workersCount int, stopCh <-chan struct{}, ca
|
||||
pt: pt,
|
||||
},
|
||||
}
|
||||
shards[i].m.init(&shards[i])
|
||||
shards[i].m.init(&shards[i].stateSizeBudget)
|
||||
}
|
||||
|
||||
ptp := &pipeTopProcessor{
|
||||
@@ -135,7 +133,7 @@ type pipeTopProcessorShardNopad struct {
|
||||
pt *pipeTop
|
||||
|
||||
// m holds per-value hits.
|
||||
m pipeTopMap
|
||||
m hitsMap
|
||||
|
||||
// keyBuf is a temporary buffer for building keys for m.
|
||||
keyBuf []byte
|
||||
@@ -148,134 +146,6 @@ type pipeTopProcessorShardNopad struct {
|
||||
stateSizeBudget int
|
||||
}
|
||||
|
||||
type pipeTopMap struct {
|
||||
shard *pipeTopProcessorShard
|
||||
|
||||
u64 map[uint64]*uint64
|
||||
negative64 map[uint64]*uint64
|
||||
strings map[string]*uint64
|
||||
|
||||
// a reduces memory allocations when counting the number of hits over big number of unique values.
|
||||
a chunkedAllocator
|
||||
}
|
||||
|
||||
func (ptm *pipeTopMap) reset() {
|
||||
ptm.shard = nil
|
||||
|
||||
ptm.u64 = nil
|
||||
ptm.negative64 = nil
|
||||
ptm.strings = nil
|
||||
}
|
||||
|
||||
func (ptm *pipeTopMap) init(shard *pipeTopProcessorShard) {
|
||||
ptm.shard = shard
|
||||
|
||||
ptm.u64 = make(map[uint64]*uint64)
|
||||
ptm.negative64 = make(map[uint64]*uint64)
|
||||
ptm.strings = make(map[string]*uint64)
|
||||
}
|
||||
|
||||
func (ptm *pipeTopMap) updateStateGeneric(key string, hits uint64) {
|
||||
if n, ok := tryParseUint64(key); ok {
|
||||
ptm.updateStateUint64(n, hits)
|
||||
return
|
||||
}
|
||||
if len(key) > 0 && key[0] == '-' {
|
||||
if n, ok := tryParseInt64(key); ok {
|
||||
ptm.updateStateNegativeInt64(n, hits)
|
||||
return
|
||||
}
|
||||
}
|
||||
ptm.updateStateString(bytesutil.ToUnsafeBytes(key), hits)
|
||||
}
|
||||
|
||||
func (ptm *pipeTopMap) updateStateInt64(n int64, hits uint64) {
|
||||
if n >= 0 {
|
||||
ptm.updateStateUint64(uint64(n), hits)
|
||||
} else {
|
||||
ptm.updateStateNegativeInt64(n, hits)
|
||||
}
|
||||
}
|
||||
|
||||
func (ptm *pipeTopMap) updateStateUint64(n, hits uint64) {
|
||||
pHits := ptm.u64[n]
|
||||
if pHits != nil {
|
||||
*pHits += hits
|
||||
return
|
||||
}
|
||||
|
||||
pHits = ptm.a.newUint64()
|
||||
*pHits += hits
|
||||
ptm.u64[n] = pHits
|
||||
|
||||
ptm.shard.stateSizeBudget -= int(unsafe.Sizeof(*pHits) + unsafe.Sizeof(pHits))
|
||||
}
|
||||
|
||||
func (ptm *pipeTopMap) updateStateNegativeInt64(n int64, hits uint64) {
|
||||
pHits := ptm.negative64[uint64(n)]
|
||||
if pHits != nil {
|
||||
*pHits += hits
|
||||
return
|
||||
}
|
||||
|
||||
pHits = ptm.a.newUint64()
|
||||
*pHits += hits
|
||||
ptm.negative64[uint64(n)] = pHits
|
||||
|
||||
ptm.shard.stateSizeBudget -= int(unsafe.Sizeof(*pHits) + unsafe.Sizeof(pHits))
|
||||
}
|
||||
|
||||
func (ptm *pipeTopMap) updateStateString(key []byte, hits uint64) {
|
||||
pHits := ptm.strings[string(key)]
|
||||
if pHits != nil {
|
||||
*pHits += hits
|
||||
return
|
||||
}
|
||||
|
||||
keyCopy := ptm.a.cloneBytesToString(key)
|
||||
pHits = ptm.a.newUint64()
|
||||
*pHits += hits
|
||||
ptm.strings[keyCopy] = pHits
|
||||
|
||||
ptm.shard.stateSizeBudget -= len(keyCopy) + int(unsafe.Sizeof(keyCopy)+unsafe.Sizeof(*pHits)+unsafe.Sizeof(pHits))
|
||||
}
|
||||
|
||||
func (ptm *pipeTopMap) mergeState(src *pipeTopMap, stopCh <-chan struct{}) {
|
||||
for n, pHitsSrc := range src.u64 {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
}
|
||||
pHitsDst := ptm.u64[n]
|
||||
if pHitsDst == nil {
|
||||
ptm.u64[n] = pHitsSrc
|
||||
} else {
|
||||
*pHitsDst += *pHitsSrc
|
||||
}
|
||||
}
|
||||
for n, pHitsSrc := range src.negative64 {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
}
|
||||
pHitsDst := ptm.negative64[n]
|
||||
if pHitsDst == nil {
|
||||
ptm.negative64[n] = pHitsSrc
|
||||
} else {
|
||||
*pHitsDst += *pHitsSrc
|
||||
}
|
||||
}
|
||||
for k, pHitsSrc := range src.strings {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
}
|
||||
pHitsDst := ptm.strings[k]
|
||||
if pHitsDst == nil {
|
||||
ptm.strings[k] = pHitsSrc
|
||||
} else {
|
||||
*pHitsDst += *pHitsSrc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// writeBlock writes br to shard.
|
||||
func (shard *pipeTopProcessorShard) writeBlock(br *blockResult) {
|
||||
byFields := shard.pt.byFields
|
||||
@@ -399,10 +269,7 @@ func (ptp *pipeTopProcessor) flush() error {
|
||||
}
|
||||
|
||||
// merge state across shards in parallel
|
||||
entries, err := ptp.mergeShardsParallel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entries := ptp.mergeShardsParallel()
|
||||
if needStop(ptp.stopCh) {
|
||||
return nil
|
||||
}
|
||||
@@ -513,108 +380,44 @@ func (ptp *pipeTopProcessor) flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ptp *pipeTopProcessor) mergeShardsParallel() ([]*pipeTopEntry, error) {
|
||||
func (ptp *pipeTopProcessor) mergeShardsParallel() []*pipeTopEntry {
|
||||
limit := ptp.pt.limit
|
||||
if limit == 0 {
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
hms := make([]*hitsMap, 0, len(ptp.shards))
|
||||
for i := range ptp.shards {
|
||||
hm := &ptp.shards[i].m
|
||||
if hm.entriesCount() > 0 {
|
||||
hms = append(hms, hm)
|
||||
}
|
||||
}
|
||||
|
||||
shards := ptp.shards
|
||||
shardsLen := len(shards)
|
||||
cpusCount := cgroup.AvailableCPUs()
|
||||
|
||||
if shardsLen == 1 {
|
||||
ptm := &shards[0].m
|
||||
entries := getTopEntries(ptm, limit, ptp.stopCh)
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
perShardMaps := make([][]pipeTopMap, shardsLen)
|
||||
for i := range shards {
|
||||
wg.Add(1)
|
||||
go func(idx int) {
|
||||
defer wg.Done()
|
||||
|
||||
perCPU := make([]pipeTopMap, cpusCount)
|
||||
for i := range perCPU {
|
||||
perCPU[i].init(&shards[idx])
|
||||
}
|
||||
|
||||
ptm := &shards[idx].m
|
||||
|
||||
for n, pHits := range ptm.u64 {
|
||||
if needStop(ptp.stopCh) {
|
||||
return
|
||||
}
|
||||
k := unsafe.Slice((*byte)(unsafe.Pointer(&n)), 8)
|
||||
h := xxhash.Sum64(k)
|
||||
cpuIdx := h % uint64(len(perCPU))
|
||||
perCPU[cpuIdx].u64[n] = pHits
|
||||
}
|
||||
for n, pHits := range ptm.negative64 {
|
||||
if needStop(ptp.stopCh) {
|
||||
return
|
||||
}
|
||||
k := unsafe.Slice((*byte)(unsafe.Pointer(&n)), 8)
|
||||
h := xxhash.Sum64(k)
|
||||
cpuIdx := h % uint64(len(perCPU))
|
||||
perCPU[cpuIdx].negative64[n] = pHits
|
||||
}
|
||||
for k, pHits := range ptm.strings {
|
||||
if needStop(ptp.stopCh) {
|
||||
return
|
||||
}
|
||||
h := xxhash.Sum64(bytesutil.ToUnsafeBytes(k))
|
||||
cpuIdx := h % uint64(len(perCPU))
|
||||
perCPU[cpuIdx].strings[k] = pHits
|
||||
}
|
||||
|
||||
perShardMaps[idx] = perCPU
|
||||
ptm.reset()
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
if needStop(ptp.stopCh) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Obtain topN entries per each shard
|
||||
entriess := make([][]*pipeTopEntry, cpusCount)
|
||||
for i := range entriess {
|
||||
wg.Add(1)
|
||||
go func(cpuIdx int) {
|
||||
defer wg.Done()
|
||||
|
||||
ptm := &perShardMaps[0][cpuIdx]
|
||||
for _, perCPU := range perShardMaps[1:] {
|
||||
ptm.mergeState(&perCPU[cpuIdx], ptp.stopCh)
|
||||
perCPU[cpuIdx].reset()
|
||||
}
|
||||
|
||||
entriess[cpuIdx] = getTopEntries(ptm, limit, ptp.stopCh)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
if needStop(ptp.stopCh) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// merge entriess
|
||||
entries := entriess[0]
|
||||
for _, es := range entriess[1:] {
|
||||
var entries []*pipeTopEntry
|
||||
var entriesLock sync.Mutex
|
||||
hitsMapMergeParallel(hms, cpusCount, ptp.stopCh, func(hm *hitsMap) {
|
||||
es := getTopEntries(hm, limit, ptp.stopCh)
|
||||
entriesLock.Lock()
|
||||
entries = append(entries, es...)
|
||||
entriesLock.Unlock()
|
||||
})
|
||||
if needStop(ptp.stopCh) {
|
||||
return nil
|
||||
}
|
||||
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
return entries[j].less(entries[i])
|
||||
})
|
||||
if uint64(len(entries)) > limit {
|
||||
entries = entries[:limit]
|
||||
}
|
||||
return entries, nil
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
func getTopEntries(ptm *pipeTopMap, limit uint64, stopCh <-chan struct{}) []*pipeTopEntry {
|
||||
func getTopEntries(hm *hitsMap, limit uint64, stopCh <-chan struct{}) []*pipeTopEntry {
|
||||
if limit == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -646,21 +449,21 @@ func getTopEntries(ptm *pipeTopMap, limit uint64, stopCh <-chan struct{}) []*pip
|
||||
}
|
||||
|
||||
var b []byte
|
||||
for n, pHits := range ptm.u64 {
|
||||
for n, pHits := range hm.u64 {
|
||||
if needStop(stopCh) {
|
||||
return nil
|
||||
}
|
||||
b = marshalUint64String(b[:0], n)
|
||||
pushEntry(bytesutil.ToUnsafeString(b), *pHits, true)
|
||||
}
|
||||
for n, pHits := range ptm.negative64 {
|
||||
for n, pHits := range hm.negative64 {
|
||||
if needStop(stopCh) {
|
||||
return nil
|
||||
}
|
||||
b = marshalInt64String(b[:0], int64(n))
|
||||
pushEntry(bytesutil.ToUnsafeString(b), *pHits, true)
|
||||
}
|
||||
for k, pHits := range ptm.strings {
|
||||
for k, pHits := range hm.strings {
|
||||
if needStop(stopCh) {
|
||||
return nil
|
||||
}
|
||||
|
||||
103
lib/logstorage/pipe_union.go
Normal file
103
lib/logstorage/pipe_union.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package logstorage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/contextutil"
|
||||
)
|
||||
|
||||
// pipeUnion processes '| union ...' pipe.
|
||||
//
|
||||
// See https://docs.victoriametrics.com/victorialogs/logsql/#union-pipe
|
||||
type pipeUnion struct {
|
||||
// q is a query for obtaining results to add after all the input results
|
||||
q *Query
|
||||
|
||||
// runUnionQuery must be initialized by the caller via initUnionQuery before query execution
|
||||
runUnionQuery func(ctx context.Context, q *Query, writeBlock func(workerID uint, br *blockResult)) error
|
||||
}
|
||||
|
||||
func (pu *pipeUnion) initUnionQuery(runUnionQuery func(ctx context.Context, q *Query, writeblock func(workerID uint, br *blockResult)) error) pipe {
|
||||
puNew := *pu
|
||||
puNew.runUnionQuery = runUnionQuery
|
||||
return &puNew
|
||||
}
|
||||
|
||||
func (pu *pipeUnion) String() string {
|
||||
return fmt.Sprintf("union (%s)", pu.q.String())
|
||||
}
|
||||
|
||||
func (pu *pipeUnion) canLiveTail() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (pu *pipeUnion) hasFilterInWithQuery() bool {
|
||||
// The pu.q query with possible in(...) filters is processed independently at pu.flush(),
|
||||
// so return false here.
|
||||
return false
|
||||
}
|
||||
|
||||
func (pu *pipeUnion) initFilterInValues(_ *inValuesCache, _ getFieldValuesFunc) (pipe, error) {
|
||||
return pu, nil
|
||||
}
|
||||
|
||||
func (pu *pipeUnion) updateNeededFields(_, _ fieldsSet) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
func (pu *pipeUnion) newPipeProcessor(_ int, stopCh <-chan struct{}, _ func(), ppNext pipeProcessor) pipeProcessor {
|
||||
return &pipeUnionProcessor{
|
||||
pu: pu,
|
||||
stopCh: stopCh,
|
||||
ppNext: ppNext,
|
||||
}
|
||||
}
|
||||
|
||||
type pipeUnionProcessor struct {
|
||||
pu *pipeUnion
|
||||
stopCh <-chan struct{}
|
||||
ppNext pipeProcessor
|
||||
}
|
||||
|
||||
func (pup *pipeUnionProcessor) writeBlock(workerID uint, br *blockResult) {
|
||||
if br.rowsLen == 0 {
|
||||
return
|
||||
}
|
||||
pup.ppNext.writeBlock(workerID, br)
|
||||
}
|
||||
|
||||
func (pup *pipeUnionProcessor) flush() error {
|
||||
// execute the query to union
|
||||
ctxWithCancel, cancel := contextutil.NewStopChanContext(pup.stopCh)
|
||||
defer cancel()
|
||||
|
||||
return pup.pu.runUnionQuery(ctxWithCancel, pup.pu.q, pup.ppNext.writeBlock)
|
||||
}
|
||||
|
||||
func parsePipeUnion(lex *lexer) (pipe, error) {
|
||||
if !lex.isKeyword("union") {
|
||||
return nil, fmt.Errorf("unexpected token: %q; want %q", lex.token, "union")
|
||||
}
|
||||
lex.nextToken()
|
||||
|
||||
if !lex.isKeyword("(") {
|
||||
return nil, fmt.Errorf("missing '(' before the union query")
|
||||
}
|
||||
lex.nextToken()
|
||||
|
||||
q, err := parseQuery(lex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse query inside union(...): %w", err)
|
||||
}
|
||||
|
||||
if !lex.isKeyword(")") {
|
||||
return nil, fmt.Errorf("missing ')' after the union query")
|
||||
}
|
||||
lex.nextToken()
|
||||
|
||||
pu := &pipeUnion{
|
||||
q: q,
|
||||
}
|
||||
return pu, nil
|
||||
}
|
||||
44
lib/logstorage/pipe_union_test.go
Normal file
44
lib/logstorage/pipe_union_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParsePipeUnionSuccess(t *testing.T) {
|
||||
f := func(pipeStr string) {
|
||||
t.Helper()
|
||||
expectParsePipeSuccess(t, pipeStr)
|
||||
}
|
||||
|
||||
f(`union (*)`)
|
||||
f(`union (foo)`)
|
||||
f(`union (foo | union (bar | stats count(*) as x))`)
|
||||
}
|
||||
|
||||
func TestParsePipeUnionFailure(t *testing.T) {
|
||||
f := func(pipeStr string) {
|
||||
t.Helper()
|
||||
expectParsePipeFailure(t, pipeStr)
|
||||
}
|
||||
|
||||
f(`union`)
|
||||
f(`union()`)
|
||||
f(`union(foo | count)`)
|
||||
f(`union (foo) bar`)
|
||||
}
|
||||
|
||||
func TestPipeUnionUpdateNeededFields(t *testing.T) {
|
||||
f := func(s string, neededFields, unneededFields, neededFieldsExpected, unneededFieldsExpected string) {
|
||||
t.Helper()
|
||||
expectPipeNeededFields(t, s, neededFields, unneededFields, neededFieldsExpected, unneededFieldsExpected)
|
||||
}
|
||||
|
||||
// all the needed fields
|
||||
f("union (abc)", "*", "", "*", "")
|
||||
|
||||
// all the needed fields, non-empty unneeded fields
|
||||
f("union (abc)", "*", "f1,f2", "*", "f1,f2")
|
||||
|
||||
// non-empty needed fields
|
||||
f("union (abc)", "f1,f2", "", "f1,f2", "")
|
||||
}
|
||||
@@ -7,9 +7,8 @@ import (
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
||||
@@ -66,7 +65,7 @@ func (pu *pipeUniq) initFilterInValues(_ *inValuesCache, _ getFieldValuesFunc) (
|
||||
}
|
||||
|
||||
func (pu *pipeUniq) newPipeProcessor(workersCount int, stopCh <-chan struct{}, cancel func(), ppNext pipeProcessor) pipeProcessor {
|
||||
maxStateSize := int64(float64(memory.Allowed()) * 0.2)
|
||||
maxStateSize := int64(float64(memory.Allowed()) * 0.4)
|
||||
|
||||
shards := make([]pipeUniqProcessorShard, workersCount)
|
||||
for i := range shards {
|
||||
@@ -75,6 +74,7 @@ func (pu *pipeUniq) newPipeProcessor(workersCount int, stopCh <-chan struct{}, c
|
||||
pu: pu,
|
||||
},
|
||||
}
|
||||
shards[i].m.init(&shards[i].stateSizeBudget)
|
||||
}
|
||||
|
||||
pup := &pipeUniqProcessor{
|
||||
@@ -115,11 +115,8 @@ type pipeUniqProcessorShardNopad struct {
|
||||
// pu points to the parent pipeUniq.
|
||||
pu *pipeUniq
|
||||
|
||||
// a is used for reducing memory allocations when collecting unique values.
|
||||
a chunkedAllocator
|
||||
|
||||
// m holds per-row hits.
|
||||
m map[string]*uint64
|
||||
m hitsMap
|
||||
|
||||
// keyBuf is a temporary buffer for building keys for m.
|
||||
keyBuf []byte
|
||||
@@ -136,7 +133,7 @@ type pipeUniqProcessorShardNopad struct {
|
||||
//
|
||||
// It returns false if the block cannot be written because of the exceeded limit.
|
||||
func (shard *pipeUniqProcessorShard) writeBlock(br *blockResult) bool {
|
||||
if limit := shard.pu.limit; limit > 0 && uint64(len(shard.m)) > limit {
|
||||
if limit := shard.pu.limit; limit > 0 && shard.m.entriesCount() > limit {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -153,30 +150,14 @@ func (shard *pipeUniqProcessorShard) writeBlock(br *blockResult) bool {
|
||||
keyBuf = encoding.MarshalBytes(keyBuf, bytesutil.ToUnsafeBytes(c.name))
|
||||
keyBuf = encoding.MarshalBytes(keyBuf, bytesutil.ToUnsafeBytes(v))
|
||||
}
|
||||
shard.updateState(bytesutil.ToUnsafeString(keyBuf), 1)
|
||||
shard.m.updateStateString(keyBuf, 1)
|
||||
}
|
||||
shard.keyBuf = keyBuf
|
||||
return true
|
||||
}
|
||||
if len(byFields) == 1 {
|
||||
// Fast path for a single field.
|
||||
c := br.getColumnByName(byFields[0])
|
||||
if c.isConst {
|
||||
v := c.valuesEncoded[0]
|
||||
shard.updateState(v, uint64(br.rowsLen))
|
||||
return true
|
||||
}
|
||||
if c.valueType == valueTypeDict {
|
||||
c.forEachDictValueWithHits(br, shard.updateState)
|
||||
return true
|
||||
}
|
||||
|
||||
values := c.getValues(br)
|
||||
for i, v := range values {
|
||||
if needHits || i == 0 || values[i-1] != values[i] {
|
||||
shard.updateState(v, 1)
|
||||
}
|
||||
}
|
||||
shard.updateStatsSingleColumn(br, byFields[0], needHits)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -206,30 +187,61 @@ func (shard *pipeUniqProcessorShard) writeBlock(br *blockResult) bool {
|
||||
for _, values := range columnValues {
|
||||
keyBuf = encoding.MarshalBytes(keyBuf, bytesutil.ToUnsafeBytes(values[i]))
|
||||
}
|
||||
shard.updateState(bytesutil.ToUnsafeString(keyBuf), 1)
|
||||
shard.m.updateStateString(keyBuf, 1)
|
||||
}
|
||||
shard.keyBuf = keyBuf
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (shard *pipeUniqProcessorShard) updateState(v string, hits uint64) {
|
||||
m := shard.getM()
|
||||
pHits := m[v]
|
||||
if pHits == nil {
|
||||
vCopy := shard.a.cloneString(v)
|
||||
pHits = shard.a.newUint64()
|
||||
m[vCopy] = pHits
|
||||
shard.stateSizeBudget -= len(vCopy) + int(unsafe.Sizeof(vCopy)+unsafe.Sizeof(hits)+unsafe.Sizeof(pHits))
|
||||
func (shard *pipeUniqProcessorShard) updateStatsSingleColumn(br *blockResult, columnName string, needHits bool) {
|
||||
c := br.getColumnByName(columnName)
|
||||
if c.isConst {
|
||||
v := c.valuesEncoded[0]
|
||||
shard.m.updateStateGeneric(v, uint64(br.rowsLen))
|
||||
return
|
||||
}
|
||||
*pHits += hits
|
||||
}
|
||||
|
||||
func (shard *pipeUniqProcessorShard) getM() map[string]*uint64 {
|
||||
if shard.m == nil {
|
||||
shard.m = make(map[string]*uint64)
|
||||
switch c.valueType {
|
||||
case valueTypeDict:
|
||||
c.forEachDictValueWithHits(br, shard.m.updateStateGeneric)
|
||||
case valueTypeUint8:
|
||||
values := c.getValuesEncoded(br)
|
||||
for _, v := range values {
|
||||
n := unmarshalUint8(v)
|
||||
shard.m.updateStateUint64(uint64(n), 1)
|
||||
}
|
||||
case valueTypeUint16:
|
||||
values := c.getValuesEncoded(br)
|
||||
for _, v := range values {
|
||||
n := unmarshalUint16(v)
|
||||
shard.m.updateStateUint64(uint64(n), 1)
|
||||
}
|
||||
case valueTypeUint32:
|
||||
values := c.getValuesEncoded(br)
|
||||
for _, v := range values {
|
||||
n := unmarshalUint32(v)
|
||||
shard.m.updateStateUint64(uint64(n), 1)
|
||||
}
|
||||
case valueTypeUint64:
|
||||
values := c.getValuesEncoded(br)
|
||||
for _, v := range values {
|
||||
n := unmarshalUint64(v)
|
||||
shard.m.updateStateUint64(n, 1)
|
||||
}
|
||||
case valueTypeInt64:
|
||||
values := c.getValuesEncoded(br)
|
||||
for _, v := range values {
|
||||
n := unmarshalInt64(v)
|
||||
shard.m.updateStateInt64(n, 1)
|
||||
}
|
||||
default:
|
||||
values := c.getValues(br)
|
||||
for i, v := range values {
|
||||
if needHits || i == 0 || values[i-1] != v {
|
||||
shard.m.updateStateGeneric(v, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return shard.m
|
||||
}
|
||||
|
||||
func (pup *pipeUniqProcessor) writeBlock(workerID uint, br *blockResult) {
|
||||
@@ -264,10 +276,7 @@ func (pup *pipeUniqProcessor) flush() error {
|
||||
}
|
||||
|
||||
// merge state across shards in parallel
|
||||
ms, err := pup.mergeShardsParallel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hms := pup.mergeShardsParallel()
|
||||
if needStop(pup.stopCh) {
|
||||
return nil
|
||||
}
|
||||
@@ -275,12 +284,12 @@ func (pup *pipeUniqProcessor) flush() error {
|
||||
resetHits := false
|
||||
if limit := pup.pu.limit; limit > 0 {
|
||||
// Trim the number of entries according to the given limit
|
||||
entriesLen := 0
|
||||
result := ms[:0]
|
||||
for _, m := range ms {
|
||||
entriesLen += len(m)
|
||||
if uint64(entriesLen) <= limit {
|
||||
result = append(result, m)
|
||||
entriesLen := uint64(0)
|
||||
result := hms[:0]
|
||||
for _, hm := range hms {
|
||||
entriesLen += hm.entriesCount()
|
||||
if entriesLen <= limit {
|
||||
result = append(result, hm)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -288,28 +297,42 @@ func (pup *pipeUniqProcessor) flush() error {
|
||||
// since arbitrary number of unique entries and hits for these entries could be skipped.
|
||||
// It is better to return zero hits instead of misleading hits results.
|
||||
resetHits = true
|
||||
for k := range m {
|
||||
delete(m, k)
|
||||
entriesLen--
|
||||
if uint64(entriesLen) <= limit {
|
||||
for n := range hm.u64 {
|
||||
if entriesLen <= limit {
|
||||
break
|
||||
}
|
||||
delete(hm.u64, n)
|
||||
entriesLen--
|
||||
}
|
||||
if len(m) > 0 {
|
||||
result = append(result, m)
|
||||
for n := range hm.negative64 {
|
||||
if entriesLen <= limit {
|
||||
break
|
||||
}
|
||||
delete(hm.negative64, n)
|
||||
entriesLen--
|
||||
}
|
||||
for k := range hm.strings {
|
||||
if entriesLen <= limit {
|
||||
break
|
||||
}
|
||||
delete(hm.strings, k)
|
||||
entriesLen--
|
||||
}
|
||||
if hm.entriesCount() > 0 {
|
||||
result = append(result, hm)
|
||||
}
|
||||
break
|
||||
}
|
||||
ms = result
|
||||
hms = result
|
||||
}
|
||||
|
||||
// Write the calculated stats in parallel to the next pipe.
|
||||
var wg sync.WaitGroup
|
||||
for i, m := range ms {
|
||||
for i := range hms {
|
||||
wg.Add(1)
|
||||
go func(workerID uint) {
|
||||
defer wg.Done()
|
||||
pup.writeShardData(workerID, m, resetHits)
|
||||
pup.writeShardData(workerID, hms[workerID], resetHits)
|
||||
}(uint(i))
|
||||
}
|
||||
wg.Wait()
|
||||
@@ -317,31 +340,32 @@ func (pup *pipeUniqProcessor) flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pup *pipeUniqProcessor) writeShardData(workerID uint, m map[string]*uint64, resetHits bool) {
|
||||
func (pup *pipeUniqProcessor) writeShardData(workerID uint, hm *hitsMap, resetHits bool) {
|
||||
wctx := &pipeUniqWriteContext{
|
||||
workerID: workerID,
|
||||
pup: pup,
|
||||
}
|
||||
|
||||
byFields := pup.pu.byFields
|
||||
var rowFields []Field
|
||||
|
||||
addHitsFieldIfNeeded := func(dst []Field, hits uint64) []Field {
|
||||
addHitsFieldIfNeeded := func(dst []Field, pHits *uint64) []Field {
|
||||
if pup.pu.hitsFieldName == "" {
|
||||
return dst
|
||||
}
|
||||
if resetHits {
|
||||
hits = 0
|
||||
hits := uint64(0)
|
||||
if !resetHits {
|
||||
hits = *pHits
|
||||
}
|
||||
hitsStr := string(marshalUint64String(nil, hits))
|
||||
dst = append(dst, Field{
|
||||
Name: pup.pu.hitsFieldName,
|
||||
Value: hitsStr,
|
||||
Value: wctx.getUint64String(hits),
|
||||
})
|
||||
return dst
|
||||
}
|
||||
|
||||
if len(byFields) == 0 {
|
||||
for k, pHits := range m {
|
||||
for k, pHits := range hm.strings {
|
||||
if needStop(pup.stopCh) {
|
||||
return
|
||||
}
|
||||
@@ -366,25 +390,46 @@ func (pup *pipeUniqProcessor) writeShardData(workerID uint, m map[string]*uint64
|
||||
Value: bytesutil.ToUnsafeString(value),
|
||||
})
|
||||
}
|
||||
rowFields = addHitsFieldIfNeeded(rowFields, *pHits)
|
||||
rowFields = addHitsFieldIfNeeded(rowFields, pHits)
|
||||
wctx.writeRow(rowFields)
|
||||
}
|
||||
} else if len(byFields) == 1 {
|
||||
fieldName := byFields[0]
|
||||
for k, pHits := range m {
|
||||
for n, pHits := range hm.u64 {
|
||||
if needStop(pup.stopCh) {
|
||||
return
|
||||
}
|
||||
rowFields = append(rowFields[:0], Field{
|
||||
Name: fieldName,
|
||||
Value: wctx.getUint64String(n),
|
||||
})
|
||||
rowFields = addHitsFieldIfNeeded(rowFields, pHits)
|
||||
wctx.writeRow(rowFields)
|
||||
}
|
||||
for n, pHits := range hm.negative64 {
|
||||
if needStop(pup.stopCh) {
|
||||
return
|
||||
}
|
||||
rowFields = append(rowFields[:0], Field{
|
||||
Name: fieldName,
|
||||
Value: wctx.getInt64String(int64(n)),
|
||||
})
|
||||
rowFields = addHitsFieldIfNeeded(rowFields, pHits)
|
||||
wctx.writeRow(rowFields)
|
||||
}
|
||||
for k, pHits := range hm.strings {
|
||||
if needStop(pup.stopCh) {
|
||||
return
|
||||
}
|
||||
|
||||
rowFields = append(rowFields[:0], Field{
|
||||
Name: fieldName,
|
||||
Value: k,
|
||||
})
|
||||
rowFields = addHitsFieldIfNeeded(rowFields, *pHits)
|
||||
rowFields = addHitsFieldIfNeeded(rowFields, pHits)
|
||||
wctx.writeRow(rowFields)
|
||||
}
|
||||
} else {
|
||||
for k, pHits := range m {
|
||||
for k, pHits := range hm.strings {
|
||||
if needStop(pup.stopCh) {
|
||||
return
|
||||
}
|
||||
@@ -405,7 +450,7 @@ func (pup *pipeUniqProcessor) writeShardData(workerID uint, m map[string]*uint64
|
||||
})
|
||||
fieldIdx++
|
||||
}
|
||||
rowFields = addHitsFieldIfNeeded(rowFields, *pHits)
|
||||
rowFields = addHitsFieldIfNeeded(rowFields, pHits)
|
||||
wctx.writeRow(rowFields)
|
||||
}
|
||||
}
|
||||
@@ -413,126 +458,30 @@ func (pup *pipeUniqProcessor) writeShardData(workerID uint, m map[string]*uint64
|
||||
wctx.flush()
|
||||
}
|
||||
|
||||
func (pup *pipeUniqProcessor) mergeShardsParallel() ([]map[string]*uint64, error) {
|
||||
shards := pup.shards
|
||||
shardsLen := len(shards)
|
||||
if shardsLen == 1 {
|
||||
m := shards[0].getM()
|
||||
var ms []map[string]*uint64
|
||||
if len(m) > 0 {
|
||||
ms = append(ms, m)
|
||||
}
|
||||
return ms, nil
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
perShardMaps := make([][]map[string]*uint64, shardsLen)
|
||||
for i := range shards {
|
||||
wg.Add(1)
|
||||
go func(idx int) {
|
||||
defer wg.Done()
|
||||
|
||||
shardMaps := make([]map[string]*uint64, shardsLen)
|
||||
for i := range shardMaps {
|
||||
shardMaps[i] = make(map[string]*uint64)
|
||||
}
|
||||
|
||||
n := int64(0)
|
||||
nTotal := int64(0)
|
||||
for k, pHits := range shards[idx].getM() {
|
||||
if needStop(pup.stopCh) {
|
||||
return
|
||||
}
|
||||
h := xxhash.Sum64(bytesutil.ToUnsafeBytes(k))
|
||||
m := shardMaps[h%uint64(len(shardMaps))]
|
||||
n += updatePipeUniqMap(m, k, pHits)
|
||||
if n > stateSizeBudgetChunk {
|
||||
if nRemaining := pup.stateSizeBudget.Add(-n); nRemaining < 0 {
|
||||
return
|
||||
}
|
||||
nTotal += n
|
||||
n = 0
|
||||
}
|
||||
}
|
||||
nTotal += n
|
||||
pup.stateSizeBudget.Add(-n)
|
||||
|
||||
perShardMaps[idx] = shardMaps
|
||||
|
||||
// Clean the original map and return its state size budget back.
|
||||
shards[idx].m = nil
|
||||
pup.stateSizeBudget.Add(nTotal)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
if needStop(pup.stopCh) {
|
||||
return nil, nil
|
||||
}
|
||||
if n := pup.stateSizeBudget.Load(); n < 0 {
|
||||
return nil, fmt.Errorf("cannot calculate [%s], since it requires more than %dMB of memory", pup.pu.String(), pup.maxStateSize/(1<<20))
|
||||
}
|
||||
|
||||
// Merge per-shard entries into perShardMaps[0]
|
||||
for i := range perShardMaps {
|
||||
wg.Add(1)
|
||||
go func(idx int) {
|
||||
defer wg.Done()
|
||||
|
||||
m := perShardMaps[0][idx]
|
||||
for i := 1; i < len(perShardMaps); i++ {
|
||||
n := int64(0)
|
||||
nTotal := int64(0)
|
||||
for k, psg := range perShardMaps[i][idx] {
|
||||
if needStop(pup.stopCh) {
|
||||
return
|
||||
}
|
||||
n += updatePipeUniqMap(m, k, psg)
|
||||
if n > stateSizeBudgetChunk {
|
||||
if nRemaining := pup.stateSizeBudget.Add(-n); nRemaining < 0 {
|
||||
return
|
||||
}
|
||||
nTotal += n
|
||||
n = 0
|
||||
}
|
||||
}
|
||||
nTotal += n
|
||||
pup.stateSizeBudget.Add(-n)
|
||||
|
||||
// Clean the original map and return its state size budget back.
|
||||
perShardMaps[i][idx] = nil
|
||||
pup.stateSizeBudget.Add(nTotal)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
if needStop(pup.stopCh) {
|
||||
return nil, nil
|
||||
}
|
||||
if n := pup.stateSizeBudget.Load(); n < 0 {
|
||||
return nil, fmt.Errorf("cannot calculate [%s], since it requires more than %dMB of memory", pup.pu.String(), pup.maxStateSize/(1<<20))
|
||||
}
|
||||
|
||||
// Filter out maps without entries
|
||||
ms := perShardMaps[0]
|
||||
result := ms[:0]
|
||||
for _, m := range ms {
|
||||
if len(m) > 0 {
|
||||
result = append(result, m)
|
||||
func (pup *pipeUniqProcessor) mergeShardsParallel() []*hitsMap {
|
||||
hms := make([]*hitsMap, 0, len(pup.shards))
|
||||
for i := range pup.shards {
|
||||
hm := &pup.shards[i].m
|
||||
if hm.entriesCount() > 0 {
|
||||
hms = append(hms, hm)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func updatePipeUniqMap(m map[string]*uint64, k string, pHitsSrc *uint64) int64 {
|
||||
pHitsDst := m[k]
|
||||
if pHitsDst != nil {
|
||||
*pHitsDst += *pHitsSrc
|
||||
return 0
|
||||
cpusCount := cgroup.AvailableCPUs()
|
||||
hmsResult := make([]*hitsMap, 0, cpusCount)
|
||||
var hmsLock sync.Mutex
|
||||
hitsMapMergeParallel(hms, cpusCount, pup.stopCh, func(hm *hitsMap) {
|
||||
if hm.entriesCount() > 0 {
|
||||
hmsLock.Lock()
|
||||
hmsResult = append(hmsResult, hm)
|
||||
hmsLock.Unlock()
|
||||
}
|
||||
})
|
||||
if needStop(pup.stopCh) {
|
||||
return nil
|
||||
}
|
||||
|
||||
m[k] = pHitsSrc
|
||||
return int64(unsafe.Sizeof(k) + unsafe.Sizeof(pHitsSrc))
|
||||
return hmsResult
|
||||
}
|
||||
|
||||
type pipeUniqWriteContext struct {
|
||||
@@ -541,6 +490,8 @@ type pipeUniqWriteContext struct {
|
||||
rcs []resultColumn
|
||||
br blockResult
|
||||
|
||||
a arena
|
||||
|
||||
// rowsCount is the number of rows in the current block
|
||||
rowsCount int
|
||||
|
||||
@@ -548,6 +499,18 @@ type pipeUniqWriteContext struct {
|
||||
valuesLen int
|
||||
}
|
||||
|
||||
func (wctx *pipeUniqWriteContext) getUint64String(n uint64) string {
|
||||
bLen := len(wctx.a.b)
|
||||
wctx.a.b = marshalUint64String(wctx.a.b, n)
|
||||
return bytesutil.ToUnsafeString(wctx.a.b[bLen:])
|
||||
}
|
||||
|
||||
func (wctx *pipeUniqWriteContext) getInt64String(n int64) string {
|
||||
bLen := len(wctx.a.b)
|
||||
wctx.a.b = marshalInt64String(wctx.a.b, n)
|
||||
return bytesutil.ToUnsafeString(wctx.a.b[bLen:])
|
||||
}
|
||||
|
||||
func (wctx *pipeUniqWriteContext) writeRow(rowFields []Field) {
|
||||
rcs := wctx.rcs
|
||||
|
||||
@@ -578,25 +541,28 @@ func (wctx *pipeUniqWriteContext) writeRow(rowFields []Field) {
|
||||
}
|
||||
|
||||
wctx.rowsCount++
|
||||
if wctx.valuesLen >= 1_000_000 {
|
||||
|
||||
// The 64_000 limit provides the best performance results.
|
||||
if wctx.valuesLen >= 64_000 {
|
||||
wctx.flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (wctx *pipeUniqWriteContext) flush() {
|
||||
rcs := wctx.rcs
|
||||
br := &wctx.br
|
||||
|
||||
wctx.valuesLen = 0
|
||||
if wctx.rowsCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Flush rcs to ppNext
|
||||
br.setResultColumns(rcs, wctx.rowsCount)
|
||||
wctx.br.setResultColumns(wctx.rcs, wctx.rowsCount)
|
||||
wctx.valuesLen = 0
|
||||
wctx.rowsCount = 0
|
||||
wctx.pup.ppNext.writeBlock(wctx.workerID, br)
|
||||
br.reset()
|
||||
for i := range rcs {
|
||||
rcs[i].resetValues()
|
||||
wctx.pup.ppNext.writeBlock(wctx.workerID, &wctx.br)
|
||||
wctx.br.reset()
|
||||
for i := range wctx.rcs {
|
||||
wctx.rcs[i].resetValues()
|
||||
}
|
||||
wctx.a.reset()
|
||||
}
|
||||
|
||||
func parsePipeUniq(lex *lexer) (pipe, error) {
|
||||
|
||||
@@ -33,6 +33,7 @@ func (su *statsCountUniq) updateNeededFields(neededFields fieldsSet) {
|
||||
func (su *statsCountUniq) newStatsProcessor(a *chunkedAllocator) statsProcessor {
|
||||
sup := a.newStatsCountUniqProcessor()
|
||||
sup.a = a
|
||||
sup.m.init()
|
||||
return sup
|
||||
}
|
||||
|
||||
@@ -74,9 +75,6 @@ func (sus *statsCountUniqSet) entriesCount() uint64 {
|
||||
}
|
||||
|
||||
func (sus *statsCountUniqSet) updateStateTimestamp(ts int64) int {
|
||||
if sus.timestamps == nil {
|
||||
sus.timestamps = make(map[uint64]struct{})
|
||||
}
|
||||
_, ok := sus.timestamps[uint64(ts)]
|
||||
if ok {
|
||||
return 0
|
||||
@@ -86,9 +84,6 @@ func (sus *statsCountUniqSet) updateStateTimestamp(ts int64) int {
|
||||
}
|
||||
|
||||
func (sus *statsCountUniqSet) updateStateUint64(n uint64) int {
|
||||
if sus.u64 == nil {
|
||||
sus.u64 = make(map[uint64]struct{})
|
||||
}
|
||||
_, ok := sus.u64[n]
|
||||
if ok {
|
||||
return 0
|
||||
@@ -105,9 +100,6 @@ func (sus *statsCountUniqSet) updateStateInt64(n int64) int {
|
||||
}
|
||||
|
||||
func (sus *statsCountUniqSet) updateStateNegativeInt64(n int64) int {
|
||||
if sus.negative64 == nil {
|
||||
sus.negative64 = make(map[uint64]struct{})
|
||||
}
|
||||
_, ok := sus.negative64[uint64(n)]
|
||||
if ok {
|
||||
return 0
|
||||
@@ -129,9 +121,6 @@ func (sus *statsCountUniqSet) updateStateGeneric(a *chunkedAllocator, v string)
|
||||
}
|
||||
|
||||
func (sus *statsCountUniqSet) updateStateString(a *chunkedAllocator, v string) int {
|
||||
if sus.strings == nil {
|
||||
sus.strings = make(map[string]struct{})
|
||||
}
|
||||
_, ok := sus.strings[v]
|
||||
if ok {
|
||||
return 0
|
||||
@@ -142,13 +131,10 @@ func (sus *statsCountUniqSet) updateStateString(a *chunkedAllocator, v string) i
|
||||
}
|
||||
|
||||
func (sus *statsCountUniqSet) mergeState(src *statsCountUniqSet, stopCh <-chan struct{}) {
|
||||
mergeUint64Set(&sus.timestamps, src.timestamps, stopCh)
|
||||
mergeUint64Set(&sus.u64, src.u64, stopCh)
|
||||
mergeUint64Set(&sus.negative64, src.negative64, stopCh)
|
||||
mergeUint64Set(sus.timestamps, src.timestamps, stopCh)
|
||||
mergeUint64Set(sus.u64, src.u64, stopCh)
|
||||
mergeUint64Set(sus.negative64, src.negative64, stopCh)
|
||||
|
||||
if src.strings != nil && sus.strings == nil {
|
||||
sus.strings = make(map[string]struct{})
|
||||
}
|
||||
for k := range src.strings {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
@@ -159,11 +145,7 @@ func (sus *statsCountUniqSet) mergeState(src *statsCountUniqSet, stopCh <-chan s
|
||||
}
|
||||
}
|
||||
|
||||
func mergeUint64Set(pDst *map[uint64]struct{}, src map[uint64]struct{}, stopCh <-chan struct{}) {
|
||||
if src != nil && *pDst == nil {
|
||||
*pDst = make(map[uint64]struct{})
|
||||
}
|
||||
dst := *pDst
|
||||
func mergeUint64Set(dst map[uint64]struct{}, src map[uint64]struct{}, stopCh <-chan struct{}) {
|
||||
for n := range src {
|
||||
if needStop(stopCh) {
|
||||
return
|
||||
|
||||
@@ -33,6 +33,7 @@ func (su *statsCountUniqHash) updateNeededFields(neededFields fieldsSet) {
|
||||
func (su *statsCountUniqHash) newStatsProcessor(a *chunkedAllocator) statsProcessor {
|
||||
sup := a.newStatsCountUniqHashProcessor()
|
||||
sup.a = a
|
||||
sup.m.init()
|
||||
return sup
|
||||
}
|
||||
|
||||
@@ -74,9 +75,6 @@ func (sus *statsCountUniqHashSet) entriesCount() uint64 {
|
||||
}
|
||||
|
||||
func (sus *statsCountUniqHashSet) updateStateTimestamp(ts int64) int {
|
||||
if sus.timestamps == nil {
|
||||
sus.timestamps = make(map[uint64]struct{})
|
||||
}
|
||||
_, ok := sus.timestamps[uint64(ts)]
|
||||
if ok {
|
||||
return 0
|
||||
@@ -86,9 +84,6 @@ func (sus *statsCountUniqHashSet) updateStateTimestamp(ts int64) int {
|
||||
}
|
||||
|
||||
func (sus *statsCountUniqHashSet) updateStateUint64(n uint64) int {
|
||||
if sus.u64 == nil {
|
||||
sus.u64 = make(map[uint64]struct{})
|
||||
}
|
||||
_, ok := sus.u64[n]
|
||||
if ok {
|
||||
return 0
|
||||
@@ -105,9 +100,6 @@ func (sus *statsCountUniqHashSet) updateStateInt64(n int64) int {
|
||||
}
|
||||
|
||||
func (sus *statsCountUniqHashSet) updateStateNegativeInt64(n int64) int {
|
||||
if sus.negative64 == nil {
|
||||
sus.negative64 = make(map[uint64]struct{})
|
||||
}
|
||||
_, ok := sus.negative64[uint64(n)]
|
||||
if ok {
|
||||
return 0
|
||||
@@ -130,9 +122,6 @@ func (sus *statsCountUniqHashSet) updateStateGeneric(v string) int {
|
||||
|
||||
func (sus *statsCountUniqHashSet) updateStateString(v []byte) int {
|
||||
h := xxhash.Sum64(v)
|
||||
if sus.strings == nil {
|
||||
sus.strings = make(map[uint64]struct{})
|
||||
}
|
||||
_, ok := sus.strings[h]
|
||||
if ok {
|
||||
return 0
|
||||
@@ -142,10 +131,10 @@ func (sus *statsCountUniqHashSet) updateStateString(v []byte) int {
|
||||
}
|
||||
|
||||
func (sus *statsCountUniqHashSet) mergeState(src *statsCountUniqHashSet, stopCh <-chan struct{}) {
|
||||
mergeUint64Set(&sus.timestamps, src.timestamps, stopCh)
|
||||
mergeUint64Set(&sus.u64, src.u64, stopCh)
|
||||
mergeUint64Set(&sus.negative64, src.negative64, stopCh)
|
||||
mergeUint64Set(&sus.strings, src.strings, stopCh)
|
||||
mergeUint64Set(sus.timestamps, src.timestamps, stopCh)
|
||||
mergeUint64Set(sus.u64, src.u64, stopCh)
|
||||
mergeUint64Set(sus.negative64, src.negative64, stopCh)
|
||||
mergeUint64Set(sus.strings, src.strings, stopCh)
|
||||
}
|
||||
|
||||
func (sup *statsCountUniqHashProcessor) updateStatsForAllRows(sf statsFunc, br *blockResult) int {
|
||||
|
||||
@@ -118,6 +118,10 @@ func (s *Storage) runQuery(ctx context.Context, tenantIDs []TenantID, q *Query,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
qNew, err = s.initUnionQueries(tenantIDs, qNew)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q = qNew
|
||||
|
||||
streamIDs := q.getStreamIDs()
|
||||
@@ -548,6 +552,38 @@ type inValuesCache struct {
|
||||
m map[string][]string
|
||||
}
|
||||
|
||||
func (s *Storage) initUnionQueries(tenantIDs []TenantID, q *Query) (*Query, error) {
|
||||
if !hasUnionPipes(q.pipes) {
|
||||
return q, nil
|
||||
}
|
||||
|
||||
runUnionQuery := func(ctx context.Context, q *Query, writeBlock func(workerID uint, br *blockResult)) error {
|
||||
return s.runQuery(ctx, tenantIDs, q, writeBlock)
|
||||
}
|
||||
|
||||
pipesNew := make([]pipe, len(q.pipes))
|
||||
for i, p := range q.pipes {
|
||||
if pu, ok := p.(*pipeUnion); ok {
|
||||
p = pu.initUnionQuery(runUnionQuery)
|
||||
}
|
||||
pipesNew[i] = p
|
||||
}
|
||||
qNew := &Query{
|
||||
f: q.f,
|
||||
pipes: pipesNew,
|
||||
}
|
||||
return qNew, nil
|
||||
}
|
||||
|
||||
func hasUnionPipes(pipes []pipe) bool {
|
||||
for _, p := range pipes {
|
||||
if _, ok := p.(*pipeUnion); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type getJoinMapFunc func(q *Query, byFields []string, prefix string) (map[string][][]Field, error)
|
||||
|
||||
func (s *Storage) initJoinMaps(ctx context.Context, tenantIDs []TenantID, q *Query) (*Query, error) {
|
||||
@@ -560,8 +596,7 @@ func (s *Storage) initJoinMaps(ctx context.Context, tenantIDs []TenantID, q *Que
|
||||
}
|
||||
|
||||
pipesNew := make([]pipe, len(q.pipes))
|
||||
for i := range q.pipes {
|
||||
p := q.pipes[i]
|
||||
for i, p := range q.pipes {
|
||||
if pj, ok := p.(*pipeJoin); ok {
|
||||
pNew, err := pj.initJoinMap(getJoinMap)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user