Merge branch 'public-single-node' into pmm-6401-read-prometheus-data-files

This commit is contained in:
Aliaksandr Valialkin 2021-12-20 19:11:26 +02:00
commit c2e9be96a7
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
29 changed files with 1563 additions and 1536 deletions

View file

@ -1177,7 +1177,9 @@ See [these docs](https://docs.victoriametrics.com/guides/guide-vmcluster-multipl
* `-downsampling.period=30d:5m,180d:1h` instructs VictoriaMetrics to deduplicate samples older than 30 days with 5 minutes interval and to deduplicate samples older than 180 days with 1 hour interval.
Downsampling is applied independently per each time series. It can reduce disk space usage and improve query performance if it is applied to time series with big number of samples per each series. The downsampling doesn't improve query performance if the database contains big number of time series with small number of samples per each series (aka [high churn rate](https://docs.victoriametrics.com/FAQ.html#what-is-high-churn-rate)), since downsamlping doesn't reduce the number of time series. So the majority of time is spent on searching for the matching time series.
Downsampling is applied independently per each time series. It can reduce disk space usage and improve query performance if it is applied to time series with big number of samples per each series. The downsampling doesn't improve query performance if the database contains big number of time series with small number of samples per each series (aka [high churn rate](https://docs.victoriametrics.com/FAQ.html#what-is-high-churn-rate)), since downsampling doesn't reduce the number of time series. So the majority of time is spent on searching for the matching time series.
The downsampling can be evaluated for free by downloading and using enterprise binaries from [the releases page](https://github.com/VictoriaMetrics/VictoriaMetrics/releases).
## Multi-tenancy

View file

@ -254,7 +254,7 @@ func (c *client) sendBlockHTTP(block []byte) bool {
again:
req, err := http.NewRequest("POST", c.remoteWriteURL, bytes.NewBuffer(block))
if err != nil {
logger.Panicf("BUG: unexected error from http.NewRequest(%q): %s", c.sanitizedURL, err)
logger.Panicf("BUG: unexpected error from http.NewRequest(%q): %s", c.sanitizedURL, err)
}
h := req.Header
h.Set("User-Agent", "vmagent")

View file

@ -1,12 +1,12 @@
{
"files": {
"main.css": "./static/css/main.a33903a8.css",
"main.js": "./static/js/main.4305bd17.js",
"main.js": "./static/js/main.23f635e5.js",
"static/js/27.85f0e2b0.chunk.js": "./static/js/27.85f0e2b0.chunk.js",
"index.html": "./index.html"
},
"entrypoints": [
"static/css/main.a33903a8.css",
"static/js/main.4305bd17.js"
"static/js/main.23f635e5.js"
]
}

View file

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script defer="defer" src="./static/js/main.4305bd17.js"></script><link href="./static/css/main.a33903a8.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script defer="defer" src="./static/js/main.23f635e5.js"></script><link href="./static/css/main.a33903a8.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -15,6 +15,7 @@ const AxesLimitsConfigurator: FC = () => {
const onChangeLimit = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, axis: string, index: number) => {
const newLimits = yaxis.limits.range;
newLimits[axis][index] = +e.target.value;
if (newLimits[axis][0] === newLimits[axis][1] || newLimits[axis][0] > newLimits[axis][1]) return;
graphDispatch({type: "SET_YAXIS_LIMITS", payload: newLimits});
};
const debouncedOnChangeLimit = useCallback(debounce(onChangeLimit, 500), [yaxis.limits.range]);

View file

@ -9,7 +9,7 @@ const StepConfigurator: FC = () => {
const {customStep} = useGraphState();
const graphDispatch = useGraphDispatch();
const [error, setError] = useState(false);
const {time: {period: {step}, duration}} = useAppState();
const {time: {period: {step}}} = useAppState();
const onChangeStep = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const value = +e.target.value;
@ -28,10 +28,6 @@ const StepConfigurator: FC = () => {
graphDispatch({type: "TOGGLE_CUSTOM_STEP"});
};
useEffect(() => {
if (customStep.enable) onChangeEnableStep();
}, [duration]);
useEffect(() => {
if (!customStep.enable) graphDispatch({type: "SET_CUSTOM_STEP", payload: step || 1});
}, [step]);

View file

@ -18,7 +18,7 @@ export const useFetchQuery = (): {
liveData?: InstantMetricResult[],
error?: ErrorTypes | string,
} => {
const {query, displayType, serverUrl, time: {period}, queryControls: {nocache}} = useAppState();
const {query, displayType, serverUrl, time: {period}, queryControls: {nocache, autoRefresh}} = useAppState();
const {basicData, bearerData, authMethod} = useAuthState();
const {customStep} = useGraphState();
@ -37,7 +37,7 @@ export const useFetchQuery = (): {
}, [error]);
const needUpdateData = useMemo(() => {
if (!prevPeriod) return true;
if (!prevPeriod || autoRefresh) return true;
const duration = (prevPeriod.end - prevPeriod.start) / 3;
const factorLimit = duration / (period.end - period.start) >= 0.7;
const maxLimit = period.end > (prevPeriod.end + duration);

View file

@ -16,7 +16,7 @@ const HomeLayout: FC = () => {
const {isLoading, liveData, graphData, error} = useFetchQuery();
return (
<>
<Box id="homeLayout">
<AppBar position="static">
<Toolbar>
<Box display="flex">
@ -78,7 +78,7 @@ const HomeLayout: FC = () => {
</Box>}
</Box>
</Box>
</>
</Box>
);
};

View file

@ -1,14 +1,14 @@
import React, {FC, useEffect, useState} from "react";
import React, {FC, useEffect, useMemo, useState} from "react";
import {MetricResult} from "../../../api/types";
import LineChart from "../../LineChart/LineChart";
import {AlignedData as uPlotData, Series as uPlotSeries} from "uplot";
import Legend from "../../Legend/Legend";
import {useGraphDispatch} from "../../../state/graph/GraphStateContext";
import {useGraphDispatch, useGraphState} from "../../../state/graph/GraphStateContext";
import {getHideSeries, getLegendItem, getSeriesItem} from "../../../utils/uplot/series";
import {getLimitsYAxis, getTimeSeries} from "../../../utils/uplot/axes";
import {LegendItem} from "../../../utils/uplot/types";
import {AxisRange} from "../../../state/graph/reducer";
import GraphSettings from "../Configurator/Graph/GraphSettings";
import {useAppState} from "../../../state/common/StateContext";
export interface GraphViewProps {
data?: MetricResult[];
@ -16,16 +16,17 @@ export interface GraphViewProps {
const GraphView: FC<GraphViewProps> = ({data = []}) => {
const graphDispatch = useGraphDispatch();
const {time: {period}} = useAppState();
const { customStep } = useGraphState();
const currentStep = useMemo(() => customStep.enable ? customStep.value : period.step || 1, [period.step, customStep]);
const [dataChart, setDataChart] = useState<uPlotData>([[]]);
const [series, setSeries] = useState<uPlotSeries[]>([]);
const [legend, setLegend] = useState<LegendItem[]>([]);
const [hideSeries, setHideSeries] = useState<string[]>([]);
const [valuesLimit, setValuesLimit] = useState<AxisRange>({"1": [0, 1]});
const setLimitsYaxis = (values: {[key: string]: number[]}) => {
const limits = getLimitsYAxis(values);
setValuesLimit(limits);
graphDispatch({type: "SET_YAXIS_LIMITS", payload: limits});
};
@ -50,9 +51,12 @@ const GraphView: FC<GraphViewProps> = ({data = []}) => {
});
});
const timeSeries = getTimeSeries(tempTimes);
const timeSeries = getTimeSeries(tempTimes, currentStep, period);
setDataChart([timeSeries, ...data.map(d => {
return new Array(timeSeries.length).fill(1).map((v, i) => d.values[i] ? +d.values[i][1] : null);
return timeSeries.map(t => {
const value = d.values.find(v => v[0] === t);
return value ? +value[1] : null;
});
})] as uPlotData);
setLimitsYaxis(tempValues);
@ -79,7 +83,7 @@ const GraphView: FC<GraphViewProps> = ({data = []}) => {
{(data.length > 0)
? <div>
<GraphSettings/>
<LineChart data={dataChart} series={series} metrics={data} limits={valuesLimit}/>
<LineChart data={dataChart} series={series} metrics={data}/>
<Legend labels={legend} onChange={onChangeLegend}/>
</div>
: <div style={{textAlign: "center"}}>No data to show</div>}

View file

@ -11,29 +11,28 @@ import {limitsDurations} from "../../utils/time";
import throttle from "lodash.throttle";
import "uplot/dist/uPlot.min.css";
import "./tooltip.css";
import {AxisRange} from "../../state/graph/reducer";
import useResize from "../../hooks/useResize";
export interface LineChartProps {
metrics: MetricResult[];
data: uPlotData;
series: uPlotSeries[];
limits: AxisRange;
}
enum typeChartUpdate {xRange = "xRange", yRange = "yRange", data = "data"}
const LineChart: FC<LineChartProps> = ({data, series, metrics = [], limits}) => {
const LineChart: FC<LineChartProps> = ({data, series, metrics = []}) => {
const dispatch = useAppDispatch();
const {time: {period}} = useAppState();
const {yaxis} = useGraphState();
const containerRef = useRef<HTMLDivElement>(null);
const uPlotRef = useRef<HTMLDivElement>(null);
const [isPanning, setPanning] = useState(false);
const [xRange, setXRange] = useState({min: period.start, max: period.end});
const [uPlotInst, setUPlotInst] = useState<uPlot>();
const layoutSize = useResize(document.getElementById("homeLayout"));
const tooltip = document.createElement("div");
tooltip.className = "u-tooltip";
const tooltipIdx = {seriesIdx: 1, dataIdx: 0};
const tooltipIdx: {seriesIdx: number | null, dataIdx: number | undefined} = {seriesIdx: null, dataIdx: undefined};
const tooltipOffset = {left: 0, top: 0};
const setScale = ({min, max}: { min: number, max: number }): void => {
@ -73,22 +72,22 @@ const LineChart: FC<LineChartProps> = ({data, series, metrics = [], limits}) =>
const setCursor = (u: uPlot) => {
if (tooltipIdx.dataIdx === u.cursor.idx) return;
tooltipIdx.dataIdx = u.cursor.idx || 0;
if (tooltipIdx.seriesIdx && tooltipIdx.dataIdx) {
if (tooltipIdx.seriesIdx !== null && tooltipIdx.dataIdx !== undefined) {
setTooltip({u, tooltipIdx, metrics, series, tooltip, tooltipOffset});
}
};
const seriesFocus = (u: uPlot, sidx: (number | null)) => {
if (tooltipIdx.seriesIdx === sidx) return;
tooltipIdx.seriesIdx = sidx || 0;
sidx && tooltipIdx.dataIdx
tooltipIdx.seriesIdx = sidx;
sidx && tooltipIdx.dataIdx !== undefined
? setTooltip({u, tooltipIdx, metrics, series, tooltip, tooltipOffset})
: tooltip.style.display = "none";
};
const getRangeX = (): Range.MinMax => [xRange.min, xRange.max];
const getRangeY = (u: uPlot, min = 0, max = 1, axis: string): Range.MinMax => {
if (yaxis.limits.enable) return yaxis.limits.range[axis];
return min && max ? [min - (min * 0.05), max + (max * 0.05)] : limits[axis];
return min && max ? [min - (min * 0.25), max + (max * 0.25)] : [-1, 1];
};
const getScales = (): Scales => {
@ -104,7 +103,7 @@ const LineChart: FC<LineChartProps> = ({data, series, metrics = [], limits}) =>
series,
axes: getAxes(series),
scales: {...getScales()},
width: containerRef.current ? containerRef.current.offsetWidth : 400,
width: layoutSize.width ? layoutSize.width - 64 : 400,
plugins: [{hooks: {ready: onReadyChart, setCursor, setSeries: seriesFocus}}],
};
@ -135,13 +134,13 @@ const LineChart: FC<LineChartProps> = ({data, series, metrics = [], limits}) =>
setUPlotInst(u);
setXRange({min: period.start, max: period.end});
return u.destroy;
}, [uPlotRef.current, series]);
}, [uPlotRef.current, series, layoutSize]);
useEffect(() => updateChart(typeChartUpdate.data), [data]);
useEffect(() => updateChart(typeChartUpdate.xRange), [xRange]);
useEffect(() => updateChart(typeChartUpdate.yRange), [yaxis]);
return <div ref={containerRef} style={{pointerEvents: isPanning ? "none" : "auto", height: "500px"}}>
return <div style={{pointerEvents: isPanning ? "none" : "auto", height: "500px"}}>
<div ref={uPlotRef}/>
</div>;
};

View file

@ -0,0 +1,23 @@
import { useState, useEffect } from "react";
const useResize = (node: HTMLElement | null): {width: number, height: number} => {
const [windowSize, setWindowSize] = useState({
width: 0,
height: 0,
});
useEffect(() => {
if (!node) return;
const handleResize = () => {
setWindowSize({
width: node.offsetWidth,
height: node.offsetHeight,
});
};
window.addEventListener("resize", handleResize);
handleResize();
return () => window.removeEventListener("resize", handleResize);
}, []);
return windowSize;
};
export default useResize;

View file

@ -21,7 +21,7 @@ export interface GraphState {
export type GraphAction =
| { type: "TOGGLE_ENABLE_YAXIS_LIMITS" }
| { type: "SET_YAXIS_LIMITS", payload: { [key: string]: [number, number] } }
| { type: "SET_YAXIS_LIMITS", payload: AxisRange }
| { type: "TOGGLE_CUSTOM_STEP" }
| { type: "SET_CUSTOM_STEP", payload: number}

View file

@ -3,6 +3,7 @@ import {getMaxFromArray, getMinFromArray} from "../math";
import {roundTimeSeconds} from "../time";
import {AxisRange} from "../../state/graph/reducer";
import {formatTicks} from "./helpers";
import {TimeParams} from "../../types";
export const getAxes = (series: Series[]): Axis[] => Array.from(new Set(series.map(s => s.scale))).map(a => {
const axis = {scale: a, show: true, font: "10px Arial", values: formatTicks};
@ -11,11 +12,11 @@ export const getAxes = (series: Series[]): Axis[] => Array.from(new Set(series.m
return axis;
});
export const getTimeSeries = (times: number[]): number[] => {
export const getTimeSeries = (times: number[], defaultStep: number, period: TimeParams): number[] => {
const allTimes = Array.from(new Set(times)).sort((a, b) => a - b);
const step = getMinFromArray(allTimes.map((t, i) => allTimes[i + 1] - t));
const length = Math.ceil((period.end - period.start)/defaultStep);
const startTime = allTimes[0] || 0;
return new Array(allTimes.length).fill(startTime).map((d, i) => roundTimeSeconds(d + (step * i)));
return new Array(length*2).fill(startTime).map((d, i) => roundTimeSeconds(d + (defaultStep * i)));
};
export const getLimitsYAxis = (values: { [key: string]: number[] }): AxisRange => {
@ -24,7 +25,7 @@ export const getLimitsYAxis = (values: { [key: string]: number[] }): AxisRange =
const numbers = values[key];
const min = getMinFromArray(numbers);
const max = getMaxFromArray(numbers);
result[key] = [min - (min * 0.05), max + (max * 0.05)];
result[key] = min && max ? [min - (min * 0.25), max + (max * 0.25)] : [-1, 1];
}
return result;
};

View file

@ -15,6 +15,10 @@ export const defaultOptions = {
focus: {
prox: 30
},
points: {
size: 5.6,
width: 1.4
},
bind: {
mouseup: (): null => null,
mousedown: (): null => null,

View file

@ -10,10 +10,14 @@ export const getSeriesItem = (d: MetricResult, hideSeries: string[]): Series =>
return {
label,
dash: getDashLine(d.group),
width: 1.5,
width: 1.4,
stroke: getColorLine(d.group, label),
show: !includesHideSeries(label, d.group, hideSeries),
scale: String(d.group)
scale: String(d.group),
points: {
size: 4.2,
width: 1.4
}
};
};

View file

@ -4,6 +4,7 @@ import {getColorLine} from "./helpers";
export const setTooltip = ({u, tooltipIdx, metrics, series, tooltip, tooltipOffset}: SetupTooltip): void => {
const {seriesIdx, dataIdx} = tooltipIdx;
if (seriesIdx === null || dataIdx === undefined) return;
const dataSeries = u.data[seriesIdx][dataIdx];
const dataTime = u.data[0][dataIdx];
const metric = metrics[seriesIdx - 1]?.metric || {};

View file

@ -11,8 +11,8 @@ export interface SetupTooltip {
top: number
},
tooltipIdx: {
seriesIdx: number,
dataIdx: number
seriesIdx: number | null,
dataIdx: number | undefined
}
}

File diff suppressed because it is too large Load diff

View file

@ -1605,7 +1605,7 @@
{
"datasource": {
"type": "prometheus",
"uid": "${DS_DBAAS-TEST-T3-MEDIUM-INST}"
"uid": "$ds"
},
"exemplar": true,
"expr": "sum(rate(vm_promscrape_scrapes_total{job=~\"$job\", instance=~\"$instance\"}[$__interval]))",
@ -1616,7 +1616,7 @@
{
"datasource": {
"type": "prometheus",
"uid": "${DS_DBAAS-TEST-T3-MEDIUM-INST}"
"uid": "$ds"
},
"expr": "sum(rate(vm_promscrape_scraped_samples_sum{job=~\"$job\", instance=~\"$instance\"}[$__interval]))",
"interval": "",
@ -3429,7 +3429,7 @@
{
"datasource": {
"type": "prometheus",
"uid": "${DS_DBAAS-TEST-T3-MEDIUM-INST}"
"uid": "$ds"
},
"exemplar": true,
"expr": "sum(rate(vm_persistentqueue_write_duration_seconds_total{job=~\"$job\", instance=~\"$instance\", url=~\"$url\"}[$__rate_interval])) by (instance)",
@ -3538,7 +3538,7 @@
{
"datasource": {
"type": "prometheus",
"uid": "${DS_DBAAS-TEST-T3-MEDIUM-INST}"
"uid": "$ds"
},
"exemplar": true,
"expr": "sum(rate(vm_persistentqueue_read_duration_seconds_total{job=~\"$job\", instance=~\"$instance\", url=~\"$url\"}[$__rate_interval])) by (instance)",

View file

@ -6,6 +6,9 @@ sort: 15
## tip
## [v1.71.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.71.0)
* FEATURE: [VictoriaMetrics enterprise](https://victoriametrics.com/enterprise.html): add multi-level downsampling support. See [these docs](https://docs.victoriametrics.com/#downsampling) and [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/36).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add ability to analyze the correlation between two queries on a single graph. Just click `+Query` button, enter the second query in the newly appeared input field and press `Ctrl+Enter`. Results for both queries should be displayed simultaneously on the same graph. Every query has its own vertical scale, which is displayed on the left and the right side of the graph. Lines for the second query are dashed. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/1916).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add ability to override the interval between returned datapoints. By default it is automatically calculated depending on the selected time range and horizontal resolution of the graph. Now it is possible to override it with custom values. This may be useful during data exploration and debugging.

View file

@ -1177,7 +1177,9 @@ See [these docs](https://docs.victoriametrics.com/guides/guide-vmcluster-multipl
* `-downsampling.period=30d:5m,180d:1h` instructs VictoriaMetrics to deduplicate samples older than 30 days with 5 minutes interval and to deduplicate samples older than 180 days with 1 hour interval.
Downsampling is applied independently per each time series. It can reduce disk space usage and improve query performance if it is applied to time series with big number of samples per each series. The downsampling doesn't improve query performance if the database contains big number of time series with small number of samples per each series (aka [high churn rate](https://docs.victoriametrics.com/FAQ.html#what-is-high-churn-rate)), since downsamlping doesn't reduce the number of time series. So the majority of time is spent on searching for the matching time series.
Downsampling is applied independently per each time series. It can reduce disk space usage and improve query performance if it is applied to time series with big number of samples per each series. The downsampling doesn't improve query performance if the database contains big number of time series with small number of samples per each series (aka [high churn rate](https://docs.victoriametrics.com/FAQ.html#what-is-high-churn-rate)), since downsampling doesn't reduce the number of time series. So the majority of time is spent on searching for the matching time series.
The downsampling can be evaluated for free by downloading and using enterprise binaries from [the releases page](https://github.com/VictoriaMetrics/VictoriaMetrics/releases).
## Multi-tenancy

View file

@ -1181,7 +1181,9 @@ See [these docs](https://docs.victoriametrics.com/guides/guide-vmcluster-multipl
* `-downsampling.period=30d:5m,180d:1h` instructs VictoriaMetrics to deduplicate samples older than 30 days with 5 minutes interval and to deduplicate samples older than 180 days with 1 hour interval.
Downsampling is applied independently per each time series. It can reduce disk space usage and improve query performance if it is applied to time series with big number of samples per each series. The downsampling doesn't improve query performance if the database contains big number of time series with small number of samples per each series (aka [high churn rate](https://docs.victoriametrics.com/FAQ.html#what-is-high-churn-rate)), since downsamlping doesn't reduce the number of time series. So the majority of time is spent on searching for the matching time series.
Downsampling is applied independently per each time series. It can reduce disk space usage and improve query performance if it is applied to time series with big number of samples per each series. The downsampling doesn't improve query performance if the database contains big number of time series with small number of samples per each series (aka [high churn rate](https://docs.victoriametrics.com/FAQ.html#what-is-high-churn-rate)), since downsampling doesn't reduce the number of time series. So the majority of time is spent on searching for the matching time series.
The downsampling can be evaluated for free by downloading and using enterprise binaries from [the releases page](https://github.com/VictoriaMetrics/VictoriaMetrics/releases).
## Multi-tenancy

View file

@ -113,6 +113,9 @@ func (q *queue) mustResetFiles() {
// GetPendingBytes returns the number of pending bytes in the queue.
func (q *queue) GetPendingBytes() uint64 {
if q.readerOffset > q.writerOffset {
logger.Panicf("BUG: readerOffset=%d cannot exceed writerOffset=%d", q.readerOffset, q.writerOffset)
}
n := q.writerOffset - q.readerOffset
return n
}
@ -555,6 +558,9 @@ func (q *queue) nextChunkFileForRead() error {
if n := q.readerOffset % q.chunkFileSize; n > 0 {
q.readerOffset += q.chunkFileSize - n
}
if err := q.checkReaderWriterOffsets(); err != nil {
return err
}
q.readerLocalOffset = 0
q.readerPath = q.chunkFilePath(q.readerOffset)
r, err := filestream.Open(q.readerPath, true)
@ -598,6 +604,14 @@ func (q *queue) readFull(buf []byte) error {
}
q.readerLocalOffset += bufLen
q.readerOffset += bufLen
return q.checkReaderWriterOffsets()
}
func (q *queue) checkReaderWriterOffsets() error {
if q.readerOffset > q.writerOffset {
return fmt.Errorf("readerOffset=%d cannot exceed writerOffset=%d; it is likely persistent queue files were corrupted on unclean shutdown",
q.readerOffset, q.writerOffset)
}
return nil
}

View file

@ -82,7 +82,7 @@ func TestParseRelabelConfigsSuccess(t *testing.T) {
t.Helper()
pcs, err := ParseRelabelConfigs(rcs, false)
if err != nil {
t.Fatalf("unexected error: %s", err)
t.Fatalf("unexpected error: %s", err)
}
if !reflect.DeepEqual(pcs, pcsExpected) {
t.Fatalf("unexpected pcs; got\n%#v\nwant\n%#v", pcs, pcsExpected)

View file

@ -126,13 +126,14 @@ func (sw *ScrapeWork) canSwitchToStreamParseMode() bool {
// key returns unique identifier for the given sw.
//
// it can be used for comparing for equality for two ScrapeWork objects.
// It can be used for comparing for equality for two ScrapeWork objects.
func (sw *ScrapeWork) key() string {
// Do not take into account OriginalLabels.
key := fmt.Sprintf("ScrapeURL=%s, ScrapeInterval=%s, ScrapeTimeout=%s, HonorLabels=%v, HonorTimestamps=%v, DenyRedirects=%v, Labels=%s, "+
// Do not take into account OriginalLabels, since they can be changed with relabeling.
// Take into account JobNameOriginal in order to capture the case when the original job_name is changed via relabeling.
key := fmt.Sprintf("JobNameOriginal=%s, ScrapeURL=%s, ScrapeInterval=%s, ScrapeTimeout=%s, HonorLabels=%v, HonorTimestamps=%v, DenyRedirects=%v, Labels=%s, "+
"ProxyURL=%s, ProxyAuthConfig=%s, AuthConfig=%s, MetricRelabelConfigs=%s, SampleLimit=%d, DisableCompression=%v, DisableKeepAlive=%v, StreamParse=%v, "+
"ScrapeAlignInterval=%s, ScrapeOffset=%s, SeriesLimit=%d",
sw.ScrapeURL, sw.ScrapeInterval, sw.ScrapeTimeout, sw.HonorLabels, sw.HonorTimestamps, sw.DenyRedirects, sw.LabelsString(),
sw.jobNameOriginal, sw.ScrapeURL, sw.ScrapeInterval, sw.ScrapeTimeout, sw.HonorLabels, sw.HonorTimestamps, sw.DenyRedirects, sw.LabelsString(),
sw.ProxyURL.String(), sw.ProxyAuthConfig.String(),
sw.AuthConfig.String(), sw.MetricRelabelConfigs.String(), sw.SampleLimit, sw.DisableCompression, sw.DisableKeepAlive, sw.StreamParse,
sw.ScrapeAlignInterval, sw.ScrapeOffset, sw.SeriesLimit)

View file

@ -362,7 +362,7 @@ func TestNextRetentionDuration(t *testing.T) {
if d <= 0 {
currTime := time.Now().UTC()
nextTime := time.Now().UTC().Add(d)
t.Fatalf("unexected retention duration for retentionMonths=%f; got %s; must be %s + %f months", retentionMonths, nextTime, currTime, retentionMonths)
t.Fatalf("unexpected retention duration for retentionMonths=%f; got %s; must be %s + %f months", retentionMonths, nextTime, currTime, retentionMonths)
}
}
}