Compare commits

..

2 Commits

Author SHA1 Message Date
Max Kotliar
d0625bb77d wip 2025-08-27 23:31:37 +03:00
Max Kotliar
4163f18250 lib/configwatcher: Introduce a library responsible for configs reloading 2025-08-27 23:28:02 +03:00
61 changed files with 4220 additions and 803 deletions

View File

@@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/configwatcher"
"github.com/VictoriaMetrics/metrics"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/csvimport"
@@ -112,6 +113,7 @@ func main() {
flag.Usage = usage
envflag.Parse()
remotewrite.InitSecretFlags()
configwatcher.Init()
buildinfo.Init()
logger.Init()
timeserieslimits.Init(*maxLabelsPerTimeseries, *maxLabelNameLen, *maxLabelValueLen)
@@ -199,6 +201,7 @@ func main() {
}
protoparserutil.StopUnmarshalWorkers()
remotewrite.Stop()
configwatcher.Stop()
logger.Infof("successfully stopped vmagent in %.3f seconds", time.Since(startTime).Seconds())
}

View File

@@ -7,8 +7,8 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/common"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/remotewrite"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prommetadata"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/opentelemetry/firehose"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/opentelemetry/stream"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/protoparserutil"
@@ -68,7 +68,7 @@ func insertRows(at *auth.Token, tss []prompb.TimeSeries, mms []prompb.MetricMeta
ctx.WriteRequest.Timeseries = tssDst
var metadataTotal int
if prommetadata.IsEnabled() {
if promscrape.IsMetadataEnabled() {
var accountID, projectID uint32
if at != nil {
accountID = at.AccountID

View File

@@ -7,8 +7,8 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/remotewrite"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prommetadata"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/prometheus"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/prometheus/stream"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/protoparserutil"
@@ -36,7 +36,7 @@ func InsertHandler(at *auth.Token, req *http.Request) error {
return err
}
encoding := req.Header.Get("Content-Encoding")
return stream.Parse(req.Body, defaultTimestamp, encoding, true, prommetadata.IsEnabled(), func(rows []prometheus.Row, mms []prometheus.Metadata) error {
return stream.Parse(req.Body, defaultTimestamp, encoding, true, promscrape.IsMetadataEnabled(), func(rows []prometheus.Row, mms []prometheus.Metadata) error {
return insertRows(at, rows, mms, extraLabels)
}, func(s string) {
httpserver.LogError(req, s)

View File

@@ -6,8 +6,8 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/common"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/remotewrite"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prommetadata"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/promremotewrite/stream"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/protoparserutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/tenantmetrics"
@@ -71,7 +71,7 @@ func insertRows(at *auth.Token, timeseries []prompb.TimeSeries, mms []prompb.Met
ctx.WriteRequest.Timeseries = tssDst
var metadataTotal int
if prommetadata.IsEnabled() {
if promscrape.IsMetadataEnabled() {
var accountID, projectID uint32
if at != nil {
accountID = at.AccountID

View File

@@ -6,8 +6,8 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/common"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/relabel"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prommetadata"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/prometheus"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/prometheus/stream"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/protoparserutil"
@@ -30,7 +30,7 @@ func InsertHandler(req *http.Request) error {
return err
}
encoding := req.Header.Get("Content-Encoding")
return stream.Parse(req.Body, defaultTimestamp, encoding, true, prommetadata.IsEnabled(), func(rows []prometheus.Row, _ []prometheus.Metadata) error {
return stream.Parse(req.Body, defaultTimestamp, encoding, true, promscrape.IsMetadataEnabled(), func(rows []prometheus.Row, _ []prometheus.Metadata) error {
return insertRows(rows, extraLabels)
}, func(s string) {
httpserver.LogError(req, s)

View File

@@ -142,12 +142,6 @@ func (s *series) summarize(aggrFunc aggrFunc, startTime, endTime, step int64, xF
}
func execExpr(ec *evalConfig, query string) (nextSeriesFunc, error) {
// Validate query length to prevent memory exhaustion
maxLen := searchutil.GetMaxQueryLen()
if len(query) > maxLen {
return nil, fmt.Errorf("too long query; got %d bytes; mustn't exceed `-search.maxQueryLen=%d` bytes", len(query), maxLen)
}
expr, err := graphiteql.Parse(query)
if err != nil {
return nil, fmt.Errorf("cannot parse %q: %w", query, err)

View File

@@ -4070,9 +4070,6 @@ func TestExecExprFailure(t *testing.T) {
f(`holtWintersConfidenceArea(group(time("foo.baz",15),time("foo.baz",15)))`)
f(`holtWintersConfidenceArea()`)
// too long query
f(`sumSeries(` + strings.Repeat("metric.very.long.name.that.takes.space,", 500) + `metric.final)`)
}
func compareSeries(ss, ssExpected []*series, expr graphiteql.Expr) error {

View File

@@ -2,7 +2,6 @@ package vmselect
import (
"embed"
"encoding/json"
"flag"
"fmt"
"net/http"
@@ -68,7 +67,6 @@ func Init() {
prometheus.InitMaxUniqueTimeseries(*maxConcurrentRequests)
concurrencyLimitCh = make(chan struct{}, *maxConcurrentRequests)
initVMUIConfig()
initVMAlertProxy()
}
@@ -462,11 +460,6 @@ func handleStaticAndSimpleRequests(w http.ResponseWriter, r *http.Request, path
return true
}
if strings.HasPrefix(path, "/vmui/") {
if path == "/vmui/config.json" {
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, vmuiConfig)
return true
}
if strings.HasPrefix(path, "/vmui/static/") {
// Allow clients caching static contents for long period of time, since it shouldn't change over time.
// Path to static contents (such as js and css) must be changed whenever its contents is changed.
@@ -741,34 +734,8 @@ func proxyVMAlertRequests(w http.ResponseWriter, r *http.Request) {
var (
vmalertProxyHost string
vmalertProxy *nethttputil.ReverseProxy
vmuiConfig string
)
func initVMUIConfig() {
var cfg struct {
License struct {
Type string `json:"type"`
} `json:"license"`
VMAlert struct {
Enabled bool `json:"enabled"`
} `json:"vmalert"`
}
data, err := vmuiFiles.ReadFile("vmui/config.json")
if err != nil {
logger.Fatalf("cannot read vmui default config: %s", err)
}
err = json.Unmarshal(data, &cfg)
if err != nil {
logger.Fatalf("cannot parse vmui default config: %s", err)
}
cfg.VMAlert.Enabled = len(*vmalertProxyURL) != 0
data, err = json.Marshal(&cfg)
if err != nil {
logger.Fatalf("cannot create vmui config: %s", err)
}
vmuiConfig = string(data)
}
// initVMAlertProxy must be called after flag.Parse(), since it uses command-line flags.
func initVMAlertProxy() {
if len(*vmalertProxyURL) == 0 {

View File

@@ -24,6 +24,7 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httputil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
@@ -37,6 +38,7 @@ var (
latencyOffset = flag.Duration("search.latencyOffset", time.Second*30, "The time when data points become visible in query results after the collection. "+
"It can be overridden on per-query basis via latency_offset arg. "+
"Too small value can result in incomplete last points for query results")
maxQueryLen = flagutil.NewBytes("search.maxQueryLen", 16*1024, "The maximum search query length in bytes")
maxLookback = flag.Duration("search.maxLookback", 0, "Synonym to -query.lookback-delta from Prometheus. "+
"The value is dynamically detected from interval between time series datapoints if not set. It can be overridden on per-query basis via max_lookback arg. "+
"See also '-search.maxStalenessInterval' flag, which has the same meaning due to historical reasons")
@@ -731,9 +733,8 @@ func QueryHandler(qt *querytracer.Tracer, startTime time.Time, w http.ResponseWr
step = defaultStep
}
maxLen := searchutil.GetMaxQueryLen()
if len(query) > maxLen {
return fmt.Errorf("too long query; got %d bytes; mustn't exceed `-search.maxQueryLen=%d` bytes", len(query), maxLen)
if len(query) > maxQueryLen.IntN() {
return fmt.Errorf("too long query; got %d bytes; mustn't exceed `-search.maxQueryLen=%d` bytes", len(query), maxQueryLen.N)
}
etfs, err := searchutil.GetExtraTagFilters(r)
if err != nil {
@@ -903,9 +904,8 @@ func queryRangeHandler(qt *querytracer.Tracer, startTime time.Time, w http.Respo
}
// Validate input args.
maxLen := searchutil.GetMaxQueryLen()
if len(query) > maxLen {
return fmt.Errorf("too long query; got %d bytes; mustn't exceed `-search.maxQueryLen=%d` bytes", len(query), maxLen)
if len(query) > maxQueryLen.IntN() {
return fmt.Errorf("too long query; got %d bytes; mustn't exceed `-search.maxQueryLen=%d` bytes", len(query), maxQueryLen.N)
}
if start > end {
end = start + defaultStep

View File

@@ -7,12 +7,10 @@ import (
"strings"
"time"
"github.com/VictoriaMetrics/metricsql"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httputil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
"github.com/VictoriaMetrics/metricsql"
)
var (
@@ -22,7 +20,6 @@ var (
maxStatusRequestDuration = flag.Duration("search.maxStatusRequestDuration", time.Minute*5, "The maximum duration for /api/v1/status/* requests")
maxLabelsAPIDuration = flag.Duration("search.maxLabelsAPIDuration", time.Second*5, "The maximum duration for /api/v1/labels, /api/v1/label/.../values and /api/v1/series requests. "+
"See also -search.maxLabelsAPISeries and -search.ignoreExtraFiltersAtLabelsAPI")
maxQueryLen = flagutil.NewBytes("search.maxQueryLen", 16*1024, "The maximum search query length in bytes")
)
// GetMaxQueryDuration returns the maximum duration for query from r.
@@ -230,8 +227,3 @@ func toTagFilter(dst *storage.TagFilter, src *metricsql.LabelFilter) {
dst.IsRegexp = src.IsRegexp
dst.IsNegative = src.IsNegative
}
// GetMaxQueryLen returns the current value of the search.maxQueryLen flag.
func GetMaxQueryLen() int {
return maxQueryLen.IntN()
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -36,10 +36,10 @@
<meta property="og:title" content="UI for VictoriaMetrics">
<meta property="og:url" content="https://victoriametrics.com/">
<meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data">
<script type="module" crossorigin src="./assets/index-DY3sj68d.js"></script>
<script type="module" crossorigin src="./assets/index-SqjehVXD.js"></script>
<link rel="modulepreload" crossorigin href="./assets/vendor-DBOs1yKE.js">
<link rel="stylesheet" crossorigin href="./assets/vendor-D1GxaB_c.css">
<link rel="stylesheet" crossorigin href="./assets/index-XlRqIMog.css">
<link rel="stylesheet" crossorigin href="./assets/index-B7vIex3g.css">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -683,7 +683,7 @@ func writeStorageMetrics(w io.Writer, strg *storage.Storage) {
metrics.WriteCounterUint64(w, `vm_cache_eviction_bytes_total{type="storage/metricIDs", reason="miss_percentage"}`, m.MetricIDCacheMissEvictionBytes)
metrics.WriteCounterUint64(w, `vm_cache_eviction_bytes_total{type="storage/metricIDs", reason="expiration"}`, m.MetricIDCacheExpireEvictionBytes)
metrics.WriteCounterUint64(w, `vm_deleted_metrics_total{type="indexdb"}`, m.DeletedMetricsCount)
metrics.WriteCounterUint64(w, `vm_deleted_metrics_total{type="indexdb"}`, idbm.DeletedMetricsCount)
metrics.WriteCounterUint64(w, `vm_cache_collisions_total{type="storage/tsid"}`, m.TSIDCacheCollisions)
metrics.WriteCounterUint64(w, `vm_cache_collisions_total{type="storage/metricName"}`, m.MetricNameCacheCollisions)

View File

@@ -6,3 +6,12 @@ export enum AppType {
export const APP_TYPE = import.meta.env.VITE_APP_TYPE;
export const APP_TYPE_VM = APP_TYPE === AppType.victoriametrics;
export const APP_TYPE_ANOMALY = APP_TYPE === AppType.vmanomaly;
export const isDefaultDatasourceType = (datasourceType: string): boolean => {
switch (APP_TYPE) {
case AppType.victoriametrics:
return datasourceType == "prometheus" || datasourceType == "";
default:
return false;
}
};

View File

@@ -3,7 +3,7 @@ import { useEffect, useState } from "preact/compat";
import { ErrorTypes } from "../types";
import { APP_TYPE_VM } from "../constants/appType";
const useFetchAppConfig = () => {
const useFetchFlags = () => {
const dispatch = useAppDispatch();
const [isLoading, setIsLoading] = useState(false);
@@ -31,5 +31,5 @@ const useFetchAppConfig = () => {
return { isLoading, error };
};
export default useFetchAppConfig;
export default useFetchFlags;

View File

@@ -0,0 +1,45 @@
import { useAppDispatch, useAppState } from "../state/common/StateContext";
import { useEffect, useState } from "preact/compat";
import { ErrorTypes } from "../types";
import { APP_TYPE_VM } from "../constants/appType";
import { getUrlWithoutTenant } from "../utils/tenants";
const useFetchFlags = () => {
const { serverUrl } = useAppState();
const dispatch = useAppDispatch();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<ErrorTypes | string>("");
useEffect(() => {
const fetchFlags = async () => {
if (!serverUrl || !APP_TYPE_VM) return;
setError("");
setIsLoading(true);
try {
const url = getUrlWithoutTenant(serverUrl);
const response = await fetch(`${url}/flags`);
const data = await response.text();
const flags = data.split("\n").filter(flag => flag.trim() !== "")
.reduce((acc, flag) => {
const [keyRaw, valueRaw] = flag.split("=");
const key = keyRaw.trim().replace(/^-/, "");
acc[key.trim()] = valueRaw ? valueRaw.trim().replace(/^"(.*)"$/, "$1") : null;
return acc;
}, {} as Record<string, string|null>);
dispatch({ type: "SET_FLAGS", payload: flags });
} catch (e) {
setIsLoading(false);
if (e instanceof Error) setError(`${e.name}: ${e.message}`);
}
};
fetchFlags();
}, [serverUrl]);
return { isLoading, error };
};
export default useFetchFlags;

View File

@@ -1,11 +1,12 @@
import Header from "../Header/Header";
import { FC, useEffect } from "preact/compat";
import { Outlet, useSearchParams } from "react-router-dom";
import { Outlet, useLocation, useSearchParams } from "react-router-dom";
import qs from "qs";
import "../MainLayout/style.scss";
import { getAppModeEnable } from "../../utils/app-mode";
import classNames from "classnames";
import Footer from "../Footer/Footer";
import { routerOptions } from "../../router";
import useFetchDefaultTimezone from "../../hooks/useFetchDefaultTimezone";
import useDeviceDetect from "../../hooks/useDeviceDetect";
import ControlsAnomalyLayout from "./ControlsAnomalyLayout";
@@ -13,10 +14,17 @@ import ControlsAnomalyLayout from "./ControlsAnomalyLayout";
const AnomalyLayout: FC = () => {
const appModeEnable = getAppModeEnable();
const { isMobile } = useDeviceDetect();
const { pathname } = useLocation();
const [searchParams, setSearchParams] = useSearchParams();
useFetchDefaultTimezone();
const setDocumentTitle = () => {
const defaultTitle = "vmui for vmanomaly";
const routeTitle = routerOptions[pathname]?.title;
document.title = routeTitle ? `${routeTitle} - ${defaultTitle}` : defaultTitle;
};
// for support old links with search params
const redirectSearchToHashParams = () => {
const { search, href } = window.location;
@@ -30,6 +38,7 @@ const AnomalyLayout: FC = () => {
if (newHref !== href) window.location.replace(newHref);
};
useEffect(setDocumentTitle, [pathname]);
useEffect(redirectSearchToHashParams, []);
return <section className="vm-container">

View File

@@ -11,6 +11,7 @@ import { useFetchDashboards } from "../../pages/PredefinedPanels/hooks/useFetchD
import useDeviceDetect from "../../hooks/useDeviceDetect";
import ControlsMainLayout from "./ControlsMainLayout";
import useFetchDefaultTimezone from "../../hooks/useFetchDefaultTimezone";
import useFetchFlags from "../../hooks/useFetchFlags";
import useFetchAppConfig from "../../hooks/useFetchAppConfig";
const MainLayout: FC = () => {
@@ -22,6 +23,7 @@ const MainLayout: FC = () => {
useFetchDashboards();
useFetchDefaultTimezone();
useFetchAppConfig();
useFetchFlags();
const setDocumentTitle = () => {
const defaultTitle = "vmui";

View File

@@ -54,7 +54,7 @@
display: flex;
flex-direction: column;
align-items: stretch;
align-items: flex-start;
gap: $padding-medium;
max-width: calc(100vw - var(--scrollbar-width));
@@ -67,7 +67,7 @@
width: 100%;
font-size: 12px;
flex-direction: column;
align-items: stretch;
align-items: flex-start;
gap: $padding-medium;
@media (max-width: 500px) {

View File

@@ -1,5 +1,3 @@
import { APP_TYPE, AppType } from "../constants/appType";
const router = {
home: "/",
metrics: "/metrics",
@@ -17,6 +15,7 @@ const router = {
rawQuery: "/raw-query",
downsamplingDebug: "/downsampling-filters-debug",
retentionDebug: "/retention-filters-debug",
alerts: "/alerts",
rules: "/rules",
notifiers: "/notifiers",
};
@@ -52,23 +51,11 @@ const routerOptionsDefault = {
},
};
const getDefaultOptions = (appType: AppType) => {
switch (appType) {
case AppType.vmanomaly:
return {
title: "Anomaly exploration",
...routerOptionsDefault,
};
default:
return {
title: "Query",
...routerOptionsDefault,
};
}
};
export const routerOptions: { [key: string]: RouterOptions } = {
[router.home]: getDefaultOptions(APP_TYPE),
[router.home]: {
title: "Query",
...routerOptionsDefault,
},
[router.rawQuery]: {
title: "Raw query",
...routerOptionsDefault,
@@ -140,7 +127,10 @@ export const routerOptions: { [key: string]: RouterOptions } = {
title: "Icons",
header: {},
},
[router.anomaly]: getDefaultOptions(AppType.vmanomaly),
[router.anomaly]: {
title: "Anomaly exploration",
...routerOptionsDefault,
},
[router.query]: {
title: "Query",
...routerOptionsDefault,

View File

@@ -17,7 +17,7 @@ interface NavigationConfig {
serverUrl: string,
isEnterpriseLicense: boolean,
showPredefinedDashboards: boolean,
showAlerting: boolean,
showAlertLink: boolean,
}
/**
@@ -43,7 +43,7 @@ const getExploreNav = () => [
];
/**
* Submenu for Alerting tab
* Submenu for Alerts tab
*/
const getAlertingNav = () => [
@@ -57,14 +57,14 @@ const getAlertingNav = () => [
export const getDefaultNavigation = ({
isEnterpriseLicense,
showPredefinedDashboards,
showAlerting,
showAlertLink,
}: NavigationConfig): NavigationItem[] => [
{ value: router.home },
{ value: router.rawQuery },
{ label: "Explore", submenu: getExploreNav() },
{ label: "Tools", submenu: getToolsNav(isEnterpriseLicense) },
{ value: router.dashboards, hide: !showPredefinedDashboards },
{ value: "Alerting", submenu: getAlertingNav(), hide: !showAlerting },
{ value: "Alerting", submenu: getAlertingNav(), hide: !showAlertLink },
];
/**

View File

@@ -9,17 +9,17 @@ import { APP_TYPE, AppType } from "../constants/appType";
const useNavigationMenu = () => {
const appModeEnable = getAppModeEnable();
const { dashboardsSettings } = useDashboardsState();
const { serverUrl, appConfig } = useAppState();
const { serverUrl, flags, appConfig } = useAppState();
const isEnterpriseLicense = appConfig.license?.type === "enterprise";
const showAlerting = appConfig?.vmalert?.enabled || false;
const showAlertLink = Boolean(flags["vmalert.proxyURL"]);
const showPredefinedDashboards = Boolean(!appModeEnable && dashboardsSettings.length);
const navigationConfig = useMemo(() => ({
serverUrl,
isEnterpriseLicense,
showAlerting,
showAlertLink,
showPredefinedDashboards
}), [serverUrl, isEnterpriseLicense, showAlerting, showPredefinedDashboards]);
}), [serverUrl, isEnterpriseLicense, showAlertLink, showPredefinedDashboards]);
const menu = useMemo(() => {

View File

@@ -10,6 +10,7 @@ export interface AppState {
tenantId: string;
theme: Theme;
isDarkTheme: boolean | null;
flags: Record<string, string | null>;
appConfig: AppConfig
}
@@ -17,6 +18,7 @@ export type Action =
| { type: "SET_SERVER", payload: string }
| { type: "SET_THEME", payload: Theme }
| { type: "SET_TENANT_ID", payload: string }
| { type: "SET_FLAGS", payload: Record<string, string | null> }
| { type: "SET_APP_CONFIG", payload: AppConfig }
| { type: "SET_DARK_THEME" }
@@ -27,6 +29,7 @@ export const initialState: AppState = {
tenantId,
theme: (getFromStorage("THEME") || Theme.system) as Theme,
isDarkTheme: null,
flags: {},
appConfig: {}
};
@@ -53,6 +56,11 @@ export function reducer(state: AppState, action: Action): AppState {
...state,
isDarkTheme: isDarkTheme(state.theme)
};
case "SET_FLAGS":
return {
...state,
flags: action.payload
};
case "SET_APP_CONFIG":
return {
...state,

View File

@@ -180,9 +180,6 @@ export interface AppConfig {
license?: {
type?: "enterprise" | "opensource";
};
vmalert?: {
enabled: boolean;
};
}
export interface Group {

View File

@@ -7,8 +7,7 @@ ROOT_IMAGE ?= alpine:3.22.1
ROOT_IMAGE_SCRATCH ?= scratch
CERTS_IMAGE := alpine:3.22.1
GO_BUILDER_IMAGE := golang:1.25.0
GO_BUILDER_IMAGE := golang:1.25.0-alpine
BUILDER_IMAGE := local/builder:2.0.0-$(shell echo $(GO_BUILDER_IMAGE) | tr :/ __)-1
BASE_IMAGE := local/base:1.1.4-$(shell echo $(ROOT_IMAGE) | tr :/ __)-$(shell echo $(CERTS_IMAGE) | tr :/ __)
DOCKER ?= docker
@@ -44,7 +43,7 @@ app-via-docker: package-builder
$(BUILDER_IMAGE) \
go build $(RACE) -trimpath -buildvcs=false \
-ldflags "-extldflags '-static' $(GO_BUILDINFO)" \
-tags 'netgo osusergo $(EXTRA_GO_BUILD_TAGS)' \
-tags 'netgo osusergo musl $(EXTRA_GO_BUILD_TAGS)' \
-o bin/$(APP_NAME)$(APP_SUFFIX)-prod $(PKG_PREFIX)/app/$(APP_NAME)
app-via-docker-windows: package-builder
@@ -159,11 +158,11 @@ app-via-docker-pure:
APP_SUFFIX='-pure' DOCKER_OPTS='--env CGO_ENABLED=0' $(MAKE) app-via-docker
app-via-docker-linux-amd64:
EXTRA_DOCKER_ENVS='CC=x86_64-linux-gnu-gcc' \
EXTRA_DOCKER_ENVS='CC=/opt/cross-builder/x86_64-linux-musl-cross/bin/x86_64-linux-musl-gcc' \
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 $(MAKE) app-via-docker-goos-goarch
app-via-docker-linux-arm64:
EXTRA_DOCKER_ENVS='CC=aarch64-linux-gnu-gcc' \
EXTRA_DOCKER_ENVS='CC=/opt/cross-builder/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc' \
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 $(MAKE) app-via-docker-goos-goarch
app-via-docker-linux-arm:
@@ -202,11 +201,11 @@ package-via-docker-pure:
APP_SUFFIX='-pure' DOCKER_OPTS='--env CGO_ENABLED=0' $(MAKE) package-via-docker
package-via-docker-amd64:
EXTRA_DOCKER_ENVS='CC=x86_64-linux-gnu-gcc' \
EXTRA_DOCKER_ENVS='CC=/opt/cross-builder/x86_64-linux-musl-cross/bin/x86_64-linux-musl-gcc' \
CGO_ENABLED=1 GOARCH=amd64 $(MAKE) package-via-docker-goarch
package-via-docker-arm64:
EXTRA_DOCKER_ENVS='CC=aarch64-linux-gnu-gcc' \
EXTRA_DOCKER_ENVS='CC=/opt/cross-builder/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc' \
CGO_ENABLED=1 GOARCH=arm64 $(MAKE) package-via-docker-goarch
package-via-docker-arm:

View File

@@ -40,11 +40,7 @@ The communication scheme between components is the following:
and recording rules results back to `vmagent`;
* [alertmanager](#alertmanager) is configured to receive notifications from `vmalert`.
<picture>
<source srcset="assets/vm-single-server-dark.png" media="(prefers-color-scheme: dark)">
<source srcset="assets/vm-single-server-light.png" media="(prefers-color-scheme: light)">
<img src="assets/vm-single-server-light.png" alt="VictoriaMetrics single-server deployment" width="500" >
</picture>
<img alt="VictoriaMetrics single-server deployment" width="500" src="assets/vm-single-server.png">
To access Grafana use link [http://localhost:3000](http://localhost:3000).
@@ -82,11 +78,7 @@ The communication scheme between components is the following:
and recording rules to `vmagent`;
* [alertmanager](#alertmanager) is configured to receive notifications from `vmalert`.
<picture>
<source srcset="assets/vm-cluster-dark.png" media="(prefers-color-scheme: dark)">
<source srcset="assets/vm-cluster-light.png" media="(prefers-color-scheme: light)">
<img src="assets/vm-cluster-light.png" alt="VictoriaMetrics cluster deployment" width="500" src="assets/vm-cluster-light.png" >
</picture>
<img alt="VictoriaMetrics cluster deployment" width="500" src="assets/vm-cluster.png">
To access Grafana use link [http://localhost:3000](http://localhost:3000).

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@@ -1,5 +1,5 @@
# balance load among vminsert and vmselect instances,
# see https://docs.victoriametrics.com/victoriametrics/vmauth/#load-balancing.
# balance load among vmselects
# see https://docs.victoriametrics.com/victoriametrics/vmauth/#load-balancing
# Note: if username and password are changes, please update the Grafana datasource configuration
# and check other places where these credentials are used.
users:

View File

@@ -1,6 +1,14 @@
ARG go_builder_image=non-existing
FROM $go_builder_image
STOPSIGNAL SIGINT
RUN apt update && apt install -y \
gcc-x86-64-linux-gnu \
gcc-aarch64-linux-gnu
RUN apk add git gcc musl-dev make wget --no-cache && \
mkdir /opt/cross-builder && \
cd /opt/cross-builder && \
for arch in aarch64 x86_64; do \
wget \
https://github.com/VictoriaMetrics/muslcc-mirror/releases/download/v1.0.0/${arch}-linux-musl-cross.tgz \
-O /opt/cross-builder/${arch}-musl.tgz \
--no-verbose && \
tar zxf ${arch}-musl.tgz -C ./ && \
rm /opt/cross-builder/${arch}-musl.tgz; \
done

View File

@@ -14,12 +14,10 @@ services:
command:
- "--promscrape.config=/etc/prometheus/prometheus.yml"
- "--remoteWrite.url=http://vmauth:8427/insert/0/prometheus/api/v1/write"
- "--remoteWrite.basicAuth.username=foo"
- "--remoteWrite.basicAuth.password=bar"
restart: always
grafana:
image: grafana/grafana:12.1.1
image: grafana/grafana:12.0.2
depends_on:
- "vmauth"
ports:

View File

@@ -38,7 +38,7 @@ services:
restart: always
grafana:
image: grafana/grafana:12.1.1
image: grafana/grafana:12.0.2
depends_on:
- "victoriametrics"
ports:

View File

@@ -17,8 +17,7 @@ scrape_configs:
- job_name: vminsert
static_configs:
- targets:
- vminsert-1:8480
- vminsert-2:8480
- vminsert:8480
- job_name: vmselect
static_configs:
- targets:

View File

@@ -27,7 +27,7 @@ services:
restart: always
grafana:
image: grafana/grafana:12.1.1
image: grafana/grafana:12.0.2
depends_on:
- "victoriametrics"
ports:

View File

@@ -58,7 +58,7 @@ services:
- ./vmsingle/promscrape.yml:/promscrape.yml
grafana:
image: grafana/grafana:12.1.1
image: grafana/grafana:12.0.2
depends_on: [vmsingle]
ports:
- 3000:3000

View File

@@ -24,23 +24,15 @@ See also [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-rel
## tip
## [v1.125.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.125.0)
Released at 2025-08-29
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and [vmselect](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): apply `-search.maxQueryLen` limit to Graphite queries. Previously, this limit was only applied to Prometheus queries.
* FEATURE: upgrade Go builder from Go1.24.6 to Go1.25. See [Go1.25 release notes](https://tip.golang.org/doc/go1.25).
* FEATURE: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): add export functionality for Query (Table view) and RawQuery tabs in CSV/JSON format. See [#9332](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/9332).
* FEATURE: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): replace `Alerts` tab with `Alerting` tab in vmui. The new `Alerting` tab displays vmalert groups and rules directly in vmui interface without redirecting user to vmalert's WEB UI. Links of format `.*/prometheus/vmalert/.*` will continue working by redirecting to vmalert's UI. This functionality is available only if `-vmalert.proxyURL` is set on vmselect. Some functionality of the new `Alerting` tab requires vmalert to be of the same version as vmselect, or higher.
* FEATURE: [vmalert](https://docs.victoriametrics.com/victoriametrics/vmalert/): add `/api/v1/group?group_id=<id>` API endpoint for viewing details of a specific [rules group](https://docs.victoriametrics.com/vmalert.html#groups). The new handler is used by new `Alerting` tab in vmselect.
* FEATURE: [vmalert](https://docs.victoriametrics.com/victoriametrics/vmalert/): add `lastError` field to `/api/v1/notifiers` response. The new field contains an error message if the last attempt to send data to the notifier failed. The error can be also viewed in `Alerting. Notifiers` tab.
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): optimize `/api/v1/labels` and `/api/v1/label/TAG/values` requests with a single `match` or `extra_filters` filter containing timeseries metric name. See this PR [#9489](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/9489) for details.
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): prevent remote write ingestion stop on push error for [Google Pub/Sub](https://docs.victoriametrics.com/victoriametrics/vmagent/#writing-metrics-to-pubsub) integration.
* BUGFIX: [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/): properly handle [mTLS authorization and routing](https://docs.victoriametrics.com/victoriametrics/vmauth/#mtls-based-request-routing). Previously it didn't work. See [#29](https://github.com/VictoriaMetrics/VictoriaLogs/issues/29).
* BUGFIX: [MetricsQL](https://docs.victoriametrics.com/victoriametrics/metricsql/): fix `timestamp` function compatibility with Prometheus when used with sub-expressions such as `timestamp(sum(foo))`. The fix applies only when `-search.disableImplicitConversion` flag is set. See more in [#9527-comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/9527#issuecomment-3200646020) and [metricsql#55](https://github.com/VictoriaMetrics/metricsql/pull/55).
* BUGFIX: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): optimize subtract operation on uint64 sets. This should potentially improve index search with huge number of deleted series. See this issue [9602](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/9602) for details.
* BUGFIX: all VictoriaMetrics [enterprise](https://docs.victoriametrics.com/enterprise/) components: fix support for automatic issuing of TLS certificates for HTTPS server at `-httpListenAddr` via [Let's Encrypt service](https://letsencrypt.org/). See [these docs](https://docs.victoriametrics.com/#automatic-issuing-of-tls-certificates).
## [v1.124.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.124.0)

View File

@@ -515,9 +515,9 @@ scrape_configs:
One of the following roles can be configured to discover targets:
### Dockerswarm role: services
* `role: services`
The `role: services` discovers all Swarm services.
The `services` role discovers all Swarm services.
Each discovered target has an [`__address__`](https://docs.victoriametrics.com/victoriametrics/relabeling/#how-to-modify-scrape-urls-in-targets) label set
to `<ip>:<port>`, where `<ip>` is the endpoint's virtual IP, while the `<port>` is the published port of the service.
@@ -542,9 +542,9 @@ One of the following roles can be configured to discover targets:
* `__meta_dockerswarm_network_label_<labelname>`: each label of the network
* `__meta_dockerswarm_network_scope`: the scope of the network
### Dockerswarm role: tasks
* `role: tasks`
The `role: tasks` discovers all Swarm tasks.
The `tasks` role discovers all Swarm tasks.
Each discovered target has an [`__address__`](https://docs.victoriametrics.com/victoriametrics/relabeling/#how-to-modify-scrape-urls-in-targets) label set
to `<ip>:<port>`, where the `<ip>` is the node IP, while the `<port>` is the published port of the task.
@@ -583,9 +583,9 @@ One of the following roles can be configured to discover targets:
The `__meta_dockerswarm_network_*` meta labels are not populated for ports which are published with `mode=host`.
### Dockerswarm role: nodes
* `role: nodes`
The `role: nodes` is used to discover Swarm nodes.
The `nodes` role is used to discover Swarm nodes.
Each discovered target has an [`__address__`](https://docs.victoriametrics.com/victoriametrics/relabeling/#how-to-modify-scrape-urls-in-targets) label set
to `<ip>:<port>`, where `<ip>` is the node IP, while the `<port>` is the `port` value obtained from the `dockerswarm_sd_configs`.
@@ -1072,7 +1072,7 @@ See [these examples](https://docs.victoriametrics.com/victoriametrics/scrape_con
One of the following `role` types can be configured to discover targets:
### Kubernetes role: node
* `role: node`
The `role: node` discovers one target per cluster node.
@@ -1093,7 +1093,7 @@ One of the following `role` types can be configured to discover targets:
In addition, the `instance` label for the node will be set to the node name as retrieved from the API server.
### Kubernetes role: service
* `role: service`
The `role: service` discovers Kubernetes services.
@@ -1120,7 +1120,7 @@ One of the following `role` types can be configured to discover targets:
* `__meta_kubernetes_service_port_protocol`: Protocol of the service port for the target.
* `__meta_kubernetes_service_type`: The type of the service.
### Kubernetes role: pod
* `role: pod`
The `role: pod` discovers all pods and exposes their containers as targets.
@@ -1153,7 +1153,8 @@ One of the following `role` types can be configured to discover targets:
* `__meta_kubernetes_pod_controller_kind`: Object kind of the pod controller.
* `__meta_kubernetes_pod_controller_name`: Name of the pod controller.
### Kubernetes role: endpoints
* `role: endpoints`
The `role: endpoints` discovers targets from listed endpoints of a service.
@@ -1179,10 +1180,10 @@ One of the following `role` types can be configured to discover targets:
* `__meta_kubernetes_endpoint_address_target_kind`: Kind of the endpoint address target.
* `__meta_kubernetes_endpoint_address_target_name`: Name of the endpoint address target.
If the endpoints belong to a service, all labels of the [`role: service`](#kubernetes-role--service) are attached.
For all targets backed by a pod, all labels of the [`role: pod`](#kubernetes-role--pod) are attached.
If the endpoints belong to a service, all labels of the `role: service` are attached.
For all targets backed by a pod, all labels of the `role: pod` are attached.
### Kubernetes role: endpointslice
* `role: endpointslice`
The `role: endpointslice` discovers targets from existing endpointslices.
@@ -1208,10 +1209,10 @@ One of the following `role` types can be configured to discover targets:
* `__meta_kubernetes_endpointslice_port_name`: Named port of the referenced endpoint.
* `__meta_kubernetes_endpointslice_port_protocol`: Protocol of the referenced endpoint.
If the endpoints belong to a service, all labels of the [`role: service`](#kubernetes-role--service) are attached.
For all targets backed by a pod, all labels of the [`role: pod`](#kubernetes-role--pod) are attached.
If the endpoints belong to a service, all labels of the `role: service` are attached.
For all targets backed by a pod, all labels of the `role: pod` are attached.
### Kubernetes role: ingress
* `role: ingress`
The `role: ingress` discovers a target for each path of each ingress.
@@ -1468,9 +1469,9 @@ scrape_configs:
# ...
```
One of the following `role` types can be configured to discover OpenStack targets:
One of the following `role` types can be configured to discover targets:
### OpenStack role: hypervisor
* `role: hypervisor`
The `role: hypervisor` discovers one target per Nova hypervisor node.
@@ -1486,7 +1487,7 @@ One of the following `role` types can be configured to discover OpenStack target
* `__meta_openstack_hypervisor_status`: the hypervisor node's status.
* `__meta_openstack_hypervisor_type`: the hypervisor node's type.
### OpenStack role: instance
* `role: instance`
The `role: instance` discovers one target per network interface of Nova instance.

View File

@@ -0,0 +1,121 @@
package configwatcher
import (
"flag"
"sync"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/procutil"
)
// TBD: migrate /-/reload handler to the package
// TBD: print registered configs in 10s after the start
// TBD: print what reload methods enabled
type handler struct {
flag string
handler func()
}
var configCheckInterval = flag.Duration("configCheckInterval", 0, "TBD")
var signalHandlers []handler
var checkIntervalHandlers []handler
var mux = sync.Mutex{}
func RegisterHandler(flag string, handlerFn func()) {
RegisterSignalHandler(flag, handlerFn)
RegisterCheckIntervalHandler(flag, handlerFn)
}
func RegisterSignalHandler(flag string, handlerFn func()) {
mux.Lock()
defer mux.Unlock()
signalHandlers = append(signalHandlers, handler{
flag: flag,
handler: handlerFn,
})
}
func RegisterCheckIntervalHandler(flag string, handlerFn func()) {
mux.Lock()
defer mux.Unlock()
checkIntervalHandlers = append(checkIntervalHandlers, handler{
flag: flag,
handler: handlerFn,
})
}
func UnregisterHandler(flag string) {
mux.Lock()
defer mux.Unlock()
newCheckIntervalHandlers := make([]handler, 0, len(checkIntervalHandlers))
for _, h := range checkIntervalHandlers {
if h.flag != flag {
newCheckIntervalHandlers = append(newCheckIntervalHandlers, h)
}
}
newSignalHandlers := make([]handler, 0, len(signalHandlers))
for _, h := range signalHandlers {
if h.flag != flag {
newSignalHandlers = append(newSignalHandlers, h)
}
}
checkIntervalHandlers = newCheckIntervalHandlers
}
var stopChan chan struct{}
func Init() {
stopChan = make(chan struct{})
go func() {
sighupCh := procutil.NewSighupChan()
var tickerCh <-chan time.Time
if *configCheckInterval > 0 {
ticker := time.NewTicker(*configCheckInterval)
tickerCh = ticker.C
defer ticker.Stop()
}
for {
select {
case <-sighupCh:
mux.Lock()
for _, h := range signalHandlers {
h.handler()
}
mux.Unlock()
case <-tickerCh:
mux.Lock()
for _, h := range checkIntervalHandlers {
h.handler()
}
mux.Unlock()
case <-stopChan:
return
}
}
}()
}
// Method for BC
func EnableCheckInterval(dur time.Duration) {
mux.Lock()
defer mux.Unlock()
if dur > *configCheckInterval {
*configCheckInterval = dur
}
}
// Stop stops Prometheus scraper.
func Stop() {
close(stopChan)
}

View File

@@ -1,19 +0,0 @@
package prommetadata
import "flag"
var enableMetadata = flag.Bool("enableMetadata", false, "Whether to enable metadata processing for metrics scraped from targets, received via VictoriaMetrics remote write, Prometheus remote write v1 or OpenTelemetry protocol. "+
"See also remoteWrite.maxMetadataPerBlock")
// IsEnabled reports whether metadata processing is enabled.
func IsEnabled() bool {
return *enableMetadata
}
// SetEnabled sets enableMetadata to v and returns the previous value of enableMetadata.
// This function is intended for promscrape tests.
func SetEnabled(v bool) bool {
prev := *enableMetadata
*enableMetadata = v
return prev
}

View File

@@ -52,6 +52,8 @@ import (
)
var (
enableMetadata = flag.Bool("enableMetadata", false, "Whether to enable metadata processing for metrics scraped from targets, received via VictoriaMetrics remote write, Prometheus remote write v1 or OpenTelemetry protocol. "+
"See also remoteWrite.maxMetadataPerBlock")
noStaleMarkers = flag.Bool("promscrape.noStaleMarkers", false, "Whether to disable sending Prometheus stale markers for metrics when scrape target disappears. This option may reduce memory usage if stale markers aren't needed for your setup. This option also disables populating the scrape_series_added metric. See https://prometheus.io/docs/concepts/jobs_instances/#automatically-generated-labels-and-time-series")
seriesLimitPerTarget = flag.Int("promscrape.seriesLimitPerTarget", 0, "Optional limit on the number of unique time series a single scrape target can expose. See https://docs.victoriametrics.com/victoriametrics/vmagent/#cardinality-limiter for more info")
strictParse = flag.Bool("promscrape.config.strictParse", true, "Whether to deny unsupported fields in -promscrape.config . Set to false in order to silently skip unsupported fields")
@@ -88,6 +90,11 @@ var (
var clusterMemberID int
// IsMetadataEnabled returns true if metadata is enabled.
func IsMetadataEnabled() bool {
return *enableMetadata
}
func mustInitClusterMemberID() {
s := *clusterMemberNum
// special case for kubernetes deployment, where pod-name formatted at some-pod-name-1

View File

@@ -9,12 +9,12 @@ import (
"sync/atomic"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/configwatcher"
"github.com/VictoriaMetrics/metrics"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/procutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discovery/azure"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discovery/consul"
@@ -113,7 +113,7 @@ func runScraper(configFile string, pushData func(at *auth.Token, wr *prompb.Writ
// Register SIGHUP handler for config reload before loadConfig.
// This guarantees that the config will be re-read if the signal arrives just after loadConfig.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240
sighupCh := procutil.NewSighupChan()
//sighupCh := procutil.NewSighupChan()
logger.Infof("reading scrape configs from %q", configFile)
cfg, err := loadConfig(configFile)
@@ -152,61 +152,69 @@ func runScraper(configFile string, pushData func(at *auth.Token, wr *prompb.Writ
scs.add("yandexcloud_sd_configs", *yandexcloud.SDCheckInterval, func(cfg *Config, swsPrev []*ScrapeWork) []*ScrapeWork { return cfg.getYandexCloudSDScrapeWork(swsPrev) })
scs.add("static_configs", 0, func(cfg *Config, _ []*ScrapeWork) []*ScrapeWork { return cfg.getStaticScrapeWork() })
var tickerCh <-chan time.Time
scs.updateConfig(cfg)
if *configCheckInterval > 0 {
ticker := time.NewTicker(*configCheckInterval)
tickerCh = ticker.C
defer ticker.Stop()
}
for {
scs.updateConfig(cfg)
waitForChans:
select {
case <-sighupCh:
logger.Infof("SIGHUP received; reloading Prometheus configs from %q", configFile)
cfgNew, err := loadConfig(configFile)
if err != nil {
configReloadErrors.Inc()
configSuccess.Set(0)
logger.Errorf("cannot read %q on SIGHUP: %s; continuing with the previous config", configFile, err)
goto waitForChans
}
configSuccess.Set(1)
if !cfgNew.mustRestart(cfg) {
logger.Infof("nothing changed in %q", configFile)
goto waitForChans
}
cfg = cfgNew
marshaledData = cfg.marshal()
configData.Store(&marshaledData)
configReloads.Inc()
configTimestamp.Set(fasttime.UnixTimestamp())
case <-tickerCh:
// TBD print notice that deprecated -promscrape.configCheckInterval is used
configwatcher.EnableCheckInterval(*configCheckInterval)
configwatcher.RegisterCheckIntervalHandler("-promscrape.config", func() {
cfgNew, err := loadConfig(configFile)
if err != nil {
configReloadErrors.Inc()
configSuccess.Set(0)
logger.Errorf("cannot read %q: %s; continuing with the previous config", configFile, err)
goto waitForChans
return
}
configSuccess.Set(1)
if !cfgNew.mustRestart(cfg) {
goto waitForChans
return
}
cfg = cfgNew
marshaledData = cfg.marshal()
configData.Store(&marshaledData)
configReloads.Inc()
configTimestamp.Set(fasttime.UnixTimestamp())
case <-globalStopCh:
cfg.mustStop()
logger.Infof("stopping Prometheus scrapers")
startTime := time.Now()
scs.stop()
logger.Infof("stopped Prometheus scrapers in %.3f seconds", time.Since(startTime).Seconds())
scs.updateConfig(cfg)
})
}
configwatcher.RegisterSignalHandler("-promscrape.config", func() {
logger.Infof("SIGHUP received; reloading Prometheus configs from %q", configFile)
cfgNew, err := loadConfig(configFile)
if err != nil {
configReloadErrors.Inc()
configSuccess.Set(0)
logger.Errorf("cannot read %q on SIGHUP: %s; continuing with the previous config", configFile, err)
return
}
}
configSuccess.Set(1)
if !cfgNew.mustRestart(cfg) {
logger.Infof("nothing changed in %q", configFile)
return
}
cfg = cfgNew
marshaledData = cfg.marshal()
configData.Store(&marshaledData)
configReloads.Inc()
configTimestamp.Set(fasttime.UnixTimestamp())
scs.updateConfig(cfg)
})
go func() {
<-globalStopCh
configwatcher.UnregisterHandler("-promscrape.config")
cfg.mustStop()
logger.Infof("stopping Prometheus scrapers")
startTime := time.Now()
scs.stop()
logger.Infof("stopped Prometheus scrapers in %.3f seconds", time.Since(startTime).Seconds())
return
}()
}
var (

View File

@@ -24,7 +24,6 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/leveledbytebufferpool"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prommetadata"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutil"
@@ -521,7 +520,7 @@ func (sw *scrapeWork) processDataOneShot(scrapeTimestamp, realTimestamp int64, b
up = 0
scrapesFailed.Inc()
} else {
if prommetadata.IsEnabled() {
if IsMetadataEnabled() {
wc.rows, wc.metadataRows = parser.UnmarshalWithMetadata(wc.rows, wc.metadataRows, bodyString, sw.logError)
} else {
wc.rows.UnmarshalWithErrLogger(bodyString, sw.logError)
@@ -617,7 +616,7 @@ func (sw *scrapeWork) processDataInStreamMode(scrapeTimestamp, realTimestamp int
areIdenticalSeries := areIdenticalSeries(cfg, lastScrapeStr, bodyString)
r := body.NewReader()
err := stream.Parse(r, scrapeTimestamp, "", false, prommetadata.IsEnabled(), func(rows []parser.Row, mms []parser.Metadata) error {
err := stream.Parse(r, scrapeTimestamp, "", false, IsMetadataEnabled(), func(rows []parser.Row, mms []parser.Metadata) error {
labelsLen := maxLabelsLen.Load()
wc := writeRequestCtxPool.Get(int(labelsLen))
defer func() {

View File

@@ -11,7 +11,6 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/chunkedbuffer"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prommetadata"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutil"
@@ -145,10 +144,11 @@ func TestScrapeWorkScrapeInternalSuccess(t *testing.T) {
}
func testScrapeWorkScrapeInternalSuccess(t *testing.T, streamParse bool) {
oldMetadataEnabled := prommetadata.SetEnabled(true)
oldIsmetadataEnabled := *enableMetadata
defer func() {
prommetadata.SetEnabled(oldMetadataEnabled)
*enableMetadata = oldIsmetadataEnabled
}()
*enableMetadata = true
f := func(data string, cfg *ScrapeWork, dataExpected string, metaDataExpected []prompb.MetricMetadata) {
t.Helper()
@@ -599,10 +599,11 @@ func testScrapeWorkScrapeInternalSuccess(t *testing.T, streamParse bool) {
//
// The core parsing functionality is validated separately in TestScrapeWorkScrapeInternalSuccess.
func TestScrapeWorkScrapeInternalStreamConcurrency(t *testing.T) {
oldMetadataEnabled := prommetadata.SetEnabled(true)
oldIsmetadataEnabled := *enableMetadata
defer func() {
prommetadata.SetEnabled(oldMetadataEnabled)
*enableMetadata = oldIsmetadataEnabled
}()
*enableMetadata = true
f := func(data string, cfg *ScrapeWork, pushDataCallsExpected int64, timeseriesExpected, timeseriesExpectedDelta, metadataExpected int64) {
t.Helper()

View File

@@ -9,7 +9,6 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/chunkedbuffer"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prommetadata"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/protoparserutil"
)
@@ -131,10 +130,11 @@ func BenchmarkScrapeWorkScrapeInternalStreamBigData(b *testing.B) {
}
func BenchmarkScrapeWorkScrapeInternalOneShotWithMetadata(b *testing.B) {
oldMetadataEnabled := prommetadata.SetEnabled(true)
oldIsmetadataEnabled := *enableMetadata
defer func() {
prommetadata.SetEnabled(oldMetadataEnabled)
*enableMetadata = oldIsmetadataEnabled
}()
*enableMetadata = true
data := `
# TYPE vm_tcplistener_accepts_total counter
# HELP vm_tcplistener_accepts_total some useless help message

View File

@@ -199,6 +199,8 @@ type IndexDBMetrics struct {
TagFiltersToMetricIDsCacheRequests uint64
TagFiltersToMetricIDsCacheMisses uint64
DeletedMetricsCount uint64
IndexDBRefCount uint64
MissingTSIDsForMetricID uint64
@@ -229,6 +231,8 @@ func (db *indexDB) scheduleToDrop() {
// UpdateMetrics updates m with metrics from the db.
func (db *indexDB) UpdateMetrics(m *IndexDBMetrics) {
// global index metrics
m.DeletedMetricsCount += uint64(db.s.getDeletedMetricIDs().Len())
m.IndexBlocksWithMetricIDsProcessed = indexBlocksWithMetricIDsProcessed.Load()
m.IndexBlocksWithMetricIDsIncorrectOrder = indexBlocksWithMetricIDsIncorrectOrder.Load()
@@ -619,21 +623,18 @@ func (is *indexSearch) searchLabelNamesWithFiltersOnTimeRange(qt *querytracer.Tr
}
func (is *indexSearch) searchLabelNamesWithFiltersOnDate(qt *querytracer.Tracer, tfss []*TagFilters, date uint64, maxLabelNames, maxMetrics int) (map[string]struct{}, error) {
var filter *uint64set.Set
if !isSingleMetricNameFilter(tfss) {
filter, err := is.searchMetricIDsWithFiltersOnDate(qt, tfss, date, maxMetrics)
if err != nil {
return nil, err
}
if filter != nil && filter.Len() <= 100e3 {
// It is faster to obtain label names by metricIDs from the filter
// instead of scanning the inverted index for the matching filters.
// This should help https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2978
metricIDs := filter.AppendTo(nil)
qt.Printf("sort %d metricIDs", len(metricIDs))
lns := is.getLabelNamesForMetricIDs(qt, metricIDs, maxLabelNames)
return lns, nil
}
filter, err := is.searchMetricIDsWithFiltersOnDate(qt, tfss, date, maxMetrics)
if err != nil {
return nil, err
}
if filter != nil && filter.Len() <= 100e3 {
// It is faster to obtain label names by metricIDs from the filter
// instead of scanning the inverted index for the matching filters.
// This should help https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2978
metricIDs := filter.AppendTo(nil)
qt.Printf("sort %d metricIDs", len(metricIDs))
lns := is.getLabelNamesForMetricIDs(qt, metricIDs, maxLabelNames)
return lns, nil
}
var prevLabelName []byte
@@ -884,27 +885,23 @@ func (is *indexSearch) searchLabelValuesOnTimeRange(qt *querytracer.Tracer, labe
}
func (is *indexSearch) searchLabelValuesOnDate(qt *querytracer.Tracer, labelName string, tfss []*TagFilters, date uint64, maxLabelValues, maxMetrics int) (map[string]struct{}, error) {
filter, err := is.searchMetricIDsWithFiltersOnDate(qt, tfss, date, maxMetrics)
if err != nil {
return nil, err
}
if filter != nil && filter.Len() <= 100e3 {
// It is faster to obtain label values by metricIDs from the filter
// instead of scanning the inverted index for the matching filters.
// This should help https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2978
metricIDs := filter.AppendTo(nil)
qt.Printf("sort %d metricIDs", len(metricIDs))
lvs := is.getLabelValuesForMetricIDs(qt, labelName, metricIDs, maxLabelValues)
return lvs, nil
}
if labelName == "__name__" {
// __name__ label is encoded as empty string in indexdb.
labelName = ""
}
useCompositeScan := labelName != "" && isSingleMetricNameFilter(tfss)
var filter *uint64set.Set
if !useCompositeScan {
filter, err := is.searchMetricIDsWithFiltersOnDate(qt, tfss, date, maxMetrics)
if err != nil {
return nil, err
}
if filter != nil && filter.Len() <= 100e3 {
// It is faster to obtain label values by metricIDs from the filter
// instead of scanning the inverted index for the matching filters.
// This should help https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2978
metricIDs := filter.AppendTo(nil)
qt.Printf("sort %d metricIDs", len(metricIDs))
lvs := is.getLabelValuesForMetricIDs(qt, labelName, metricIDs, maxLabelValues)
return lvs, nil
}
}
labelNameBytes := bytesutil.ToUnsafeBytes(labelName)
if name := getCommonMetricNameForTagFilterss(tfss); len(name) > 0 && labelName != "" {
@@ -1353,7 +1350,6 @@ func (is *indexSearch) getTSDBStatus(qt *querytracer.Tracer, tfss []*TagFilters,
qt.Printf("no matching series for filter=%s", tfss)
return &TSDBStatus{}, nil
}
ts := &is.ts
kb := &is.kb
mp := &is.mp
@@ -2194,11 +2190,6 @@ func matchTagFilters(mn *MetricName, tfs []*tagFilter, kb *bytesutil.ByteBuffer)
return true, nil
}
func isSingleMetricNameFilter(tfss []*TagFilters) bool {
// We check if tfss contain only single filter which is __name__
return len(tfss) == 1 && len(tfss[0].tfs) == 1 && getMetricNameFilter(tfss[0]) != nil
}
func (is *indexSearch) searchMetricIDsWithFiltersOnDate(qt *querytracer.Tracer, tfss []*TagFilters, date uint64, maxMetrics int) (*uint64set.Set, error) {
if len(tfss) == 0 {
return nil, nil

View File

@@ -1704,10 +1704,6 @@ func TestSearchTSIDWithTimeRange(t *testing.T) {
if err := tfsMetricName.Add(nil, []byte("testMetric"), false, false); err != nil {
t.Fatalf("cannot add filter on metric name: %s", err)
}
tfsComposite := NewTagFilters()
if err := tfsComposite.Add(nil, []byte("testMetric"), false, false); err != nil {
t.Fatalf("cannot add filter: %s", err)
}
// Perform a search within a day.
// This should return the metrics for the day
@@ -1753,16 +1749,6 @@ func TestSearchTSIDWithTimeRange(t *testing.T) {
t.Fatalf("unexpected labelNames; got\n%s\nwant\n%s", got, labelNames)
}
// Check SearchLabelNames with filters on composite key and time range.
lns, err = idbCurr.SearchLabelNames(nil, []*TagFilters{tfsComposite}, tr, 10000, 1e9, noDeadline)
if err != nil {
t.Fatalf("unexpected error in SearchLabelNames(filters=%s, timeRange=%s): %s", tfs, &tr, err)
}
got = sortedSlice(lns)
if !reflect.DeepEqual(got, labelNames) {
t.Fatalf("unexpected labelNames; got\n%s\nwant\n%s", got, labelNames)
}
// Check SearchLabelValues with the specified filter.
lvs, err = idbCurr.SearchLabelValues(nil, "", []*TagFilters{tfs}, TimeRange{}, 10000, 1e9, noDeadline)
if err != nil {
@@ -1793,17 +1779,6 @@ func TestSearchTSIDWithTimeRange(t *testing.T) {
t.Fatalf("unexpected labelValues; got\n%s\nwant\n%s", got, labelValues)
}
// Check SearchLabelValues with filters on composite key and time range.
lvs, err = idbCurr.SearchLabelValues(nil, "constant", []*TagFilters{tfsComposite}, tr, 10000, 1e9, noDeadline)
if err != nil {
t.Fatalf("unexpected error in SearchLabelValues(filters=%s, timeRange=%s): %s", tfs, &tr, err)
}
got = sortedSlice(lvs)
labelValues = []string{"const"}
if !reflect.DeepEqual(got, labelValues) {
t.Fatalf("unexpected labelValues; got\n%s\nwant\n%s", got, labelValues)
}
// Perform a search across all the days, should match all metrics
tr = TimeRange{
MinTimestamp: int64(timestamp - msecPerDay*days),

View File

@@ -655,8 +655,6 @@ type Metrics struct {
MetricNamesUsageTrackerSizeBytes uint64
MetricNamesUsageTrackerSizeMaxBytes uint64
DeletedMetricsCount uint64
IndexDBMetrics IndexDBMetrics
TableMetrics TableMetrics
}
@@ -765,8 +763,6 @@ func (s *Storage) UpdateMetrics(m *Metrics) {
}
m.NextRetentionSeconds = uint64(d)
m.DeletedMetricsCount += uint64(s.getDeletedMetricIDs().Len())
idbPrev, idbCurr := s.getPrevAndCurrIndexDBs()
defer s.putPrevAndCurrIndexDBs(idbPrev, idbCurr)
idbCurr.UpdateMetrics(&m.IndexDBMetrics)

View File

@@ -1439,55 +1439,6 @@ func TestStorageDeleteSeries_CachesAreUpdatedOrReset(t *testing.T) {
assertDeletedMetricIDsCacheSize(3)
}
func TestStorageDeleteSeriesFromPrevAndCurrIndexDB(t *testing.T) {
defer testRemoveAll(t)
rng := rand.New(rand.NewSource(1))
const numSeries = 100
trPrev := TimeRange{
MinTimestamp: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
MaxTimestamp: time.Date(2020, 1, 1, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
}
mrsPrev := testGenerateMetricRowsWithPrefix(rng, numSeries, "prev", trPrev)
trCurr := TimeRange{
MinTimestamp: time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC).UnixMilli(),
MaxTimestamp: time.Date(2020, 1, 2, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
}
mrsCurr := testGenerateMetricRowsWithPrefix(rng, numSeries, "curr", trCurr)
deleteSeries := func(s *Storage, want, wantTotal int) {
t.Helper()
tfs := NewTagFilters()
if err := tfs.Add(nil, []byte(".*"), false, true); err != nil {
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
}
got, err := s.DeleteSeries(nil, []*TagFilters{tfs}, 1e9)
if err != nil {
t.Fatalf("could not delete series unexpectedly: %v", err)
}
if got != want {
t.Fatalf("unexpected number of deleted series: got %d, want %d", got, want)
}
var m Metrics
s.UpdateMetrics(&m)
if got, want := m.DeletedMetricsCount, uint64(wantTotal); got != want {
t.Fatalf("unexpected number of total deleted series: got %d, want %d", got, want)
}
}
s := MustOpenStorage(t.Name(), OpenOptions{})
defer s.MustClose()
s.AddRows(mrsPrev, defaultPrecisionBits)
s.DebugFlush()
deleteSeries(s, numSeries, numSeries)
s.mustRotateIndexDB(time.Now())
s.AddRows(mrsCurr, defaultPrecisionBits)
s.DebugFlush()
deleteSeries(s, numSeries, 2*numSeries)
}
func TestStorageRegisterMetricNamesSerial(t *testing.T) {
path := "TestStorageRegisterMetricNamesSerial"
s := MustOpenStorage(path, OpenOptions{})

View File

@@ -359,23 +359,12 @@ func (s *Set) Subtract(a *Set) {
// Fast path - nothing to subtract.
return
}
if s.Len() >= a.Len() {
a.ForEach(func(part []uint64) bool {
for _, x := range part {
s.Del(x)
}
return true
})
} else {
s.ForEach(func(part []uint64) bool {
for _, x := range part {
if a.Has(x) {
s.Del(x)
}
}
return true
})
}
a.ForEach(func(part []uint64) bool {
for _, x := range part {
s.Del(x)
}
return true
})
}
// Equal returns true if s contains the same items as a.

View File

@@ -7,8 +7,6 @@ import (
"sort"
"testing"
"time"
"github.com/google/go-cmp/cmp"
)
func TestSetOps(t *testing.T) {
@@ -760,141 +758,3 @@ func TestAddMulti(t *testing.T) {
}
f(a)
}
func TestSubtract(t *testing.T) {
f := func(a, b, want *Set) {
t.Helper()
bBefore := b.AppendTo(nil)
a.Subtract(b)
bAfter := b.AppendTo(nil)
gotValues := []uint64{}
gotValues = a.AppendTo(gotValues)
wantValues := []uint64{}
wantValues = want.AppendTo(wantValues)
if diff := cmp.Diff(wantValues, gotValues); diff != "" {
t.Fatalf("unexpected a set (-want, +got):\n%s", diff)
}
if diff := cmp.Diff(bBefore, bAfter); diff != "" {
t.Fatalf("unexpected b set (-want, +got):\n%s", diff)
}
}
s := func(start, end uint64, se ...uint64) *Set {
s := &Set{}
for i := start; i <= end; i++ {
s.Add(i)
}
if len(se)%2 != 0 {
t.Fatalf("the number of additional starts and ends must be an even number since they go in pairs")
}
for i := 0; i < len(se); i += 2 {
start := se[i]
end := se[i+1]
for j := start; j <= end; j++ {
s.Add(j)
}
}
return s
}
var a, b, want *Set
// - no overlap
// - a values before b values
// - len(a) > len(b)
a = s(1, 500_000)
b = s(500_001, 600_000)
want = s(1, 500_000)
f(a, b, want)
// - no overlap
// - a values after b values
// - len(a) > len(b)
a = s(500_001, 1_000_000)
b = s(400_000, 500_000)
want = s(500_001, 1_000_000)
f(a, b, want)
// - no overlap
// - a values before b values
// - len(a) < len(b)
a = s(400_000, 500_000)
b = s(500_001, 1_000_000)
want = s(400_000, 500_000)
f(a, b, want)
// - no overlap
// - a values after b values
// - len(a) < len(b)
a = s(500_001, 600_000)
b = s(1, 500_000)
want = s(500_001, 600_000)
f(a, b, want)
// - overlap on the left side
// - len(a) > len(b)
a = s(500_000, 1_000_000)
b = s(400_000, 600_000)
want = s(600_001, 1_000_000)
f(a, b, want)
// - overlap on the right side
// - len(a) > len(b)
a = s(1, 500_000)
b = s(400_001, 600_000)
want = s(1, 400_000)
f(a, b, want)
// - overlap on the left side
// - len(a) < len(b)
a = s(400_000, 600_000)
b = s(500_001, 1_000_000)
want = s(400_000, 500_000)
f(a, b, want)
// - overlap on the right side
// - len(a) < len(b)
a = s(400_000, 600_000)
b = s(1, 500_000)
want = s(500_001, 600_000)
f(a, b, want)
// same
a = s(1, 500_000)
b = s(1, 500_000)
want = &Set{}
f(a, b, want)
// b is a subset of a
a = s(1, 500_000)
b = s(100_001, 400_000)
want = s(1, 100_000, 400_001, 500_000)
f(a, b, want)
// a is a subset of b
a = s(100_001, 400_000)
b = s(1, 500_000)
want = &Set{}
f(a, b, want)
// a with intervals
a = s(1, 200_000, 400_000, 500_000)
b = s(150_001, 450_000)
want = s(1, 150_000, 450_001, 500_000)
f(a, b, want)
// b with intervals
a = s(1, 500_000)
b = s(200_001, 300_000, 400_001, 500_000)
want = s(1, 200_000, 300_001, 400_000)
f(a, b, want)
// subtract more than once.
a = s(1, 500_000)
b1 := s(100_001, 200_000)
want1 := s(1, 100_000, 200_001, 500_000)
b2 := s(300_001, 400_000)
want2 := s(1, 100_000, 200_001, 300_000, 400_001, 500_000)
f(a, b1, want1)
f(a, b2, want2)
}

View File

@@ -154,66 +154,6 @@ func benchmarkIntersect(b *testing.B, sa, sb *Set) {
})
}
func BenchmarkSubtract(b *testing.B) {
f := func(b *testing.B, startA, itemsCountA, startB, itemsCountB uint64) {
sa := createRangeSet(startA, int(itemsCountA))
sb := createRangeSet(startB, int(itemsCountB))
b.ReportAllocs()
b.SetBytes(int64(sa.Len() + sb.Len()))
for b.Loop() {
saCopy := sa.Clone()
saCopy.Subtract(sb)
}
}
start := uint64(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).UnixNano())
itemsACounts := []uint64{1e3, 1e4, 1e5, 1e6, 1e7}
itemsBCounts := []uint64{1e7, 1e6, 1e5, 1e4, 1e3}
for i := range len(itemsACounts) {
itemsCountA := itemsACounts[i]
itemsCountB := itemsBCounts[i]
b.Run(fmt.Sprintf("-----NoOverlap-AbeforeB-A%d-B%d", itemsCountA, itemsCountB), func(b *testing.B) {
f(b, start, itemsCountA, start+itemsCountA, itemsCountB)
})
b.Run(fmt.Sprintf("-----NoOverlap-AbeforeB-B%d-A%d", itemsCountB, itemsCountA), func(b *testing.B) {
f(b, start+itemsCountA, itemsCountB, start, itemsCountA)
})
b.Run(fmt.Sprintf("-----NoOverlap-BbeforeA-A%d-B%d", itemsCountA, itemsCountB), func(b *testing.B) {
f(b, start-itemsCountA, itemsCountA, start, itemsCountB)
})
b.Run(fmt.Sprintf("-----NoOverlap-BbeforeA-B%d-A%d", itemsCountB, itemsCountA), func(b *testing.B) {
f(b, start, itemsCountB, start-itemsCountA, itemsCountA)
})
b.Run(fmt.Sprintf("PartialOverlap-AbeforeB-A%d-B%d", itemsCountA, itemsCountB), func(b *testing.B) {
f(b, start, itemsCountA, start+itemsCountA-itemsCountB/2, itemsCountB)
})
b.Run(fmt.Sprintf("PartialOverlap-AbeforeB-B%d-A%d", itemsCountB, itemsCountA), func(b *testing.B) {
f(b, start+itemsCountA-itemsCountB/2, itemsCountB, start, itemsCountA)
})
b.Run(fmt.Sprintf("PartialOverlap-BbeforeA-A%d-B%d", itemsCountA, itemsCountB), func(b *testing.B) {
f(b, start+itemsCountB/2, itemsCountA, start, itemsCountB)
})
b.Run(fmt.Sprintf("PartialOverlap-BbeforeA-B%d-A%d", itemsCountB, itemsCountA), func(b *testing.B) {
f(b, start, itemsCountB, start+itemsCountB/2, itemsCountA)
})
b.Run(fmt.Sprintf("---FullOverlap-AbeforeB-A%d-B%d", itemsCountA, itemsCountB), func(b *testing.B) {
f(b, start, itemsCountA, start+itemsCountA-itemsCountB, itemsCountB)
})
b.Run(fmt.Sprintf("---FullOverlap-AbeforeB-B%d-A%d", itemsCountB, itemsCountA), func(b *testing.B) {
f(b, start+itemsCountA-itemsCountB, itemsCountB, start, itemsCountA)
})
b.Run(fmt.Sprintf("---FullOverlap-BbeforeA-A%d-B%d", itemsCountA, itemsCountB), func(b *testing.B) {
f(b, start+itemsCountB, itemsCountA, start, itemsCountB)
})
b.Run(fmt.Sprintf("---FullOverlap-BbeforeA-B%d-A%d", itemsCountB, itemsCountA), func(b *testing.B) {
f(b, start, itemsCountB, start+itemsCountB, itemsCountA)
})
}
}
func createRangeSet(start uint64, itemsCount int) *Set {
var s Set
for i := 0; i < itemsCount; i++ {