mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-07-14 12:50:23 +03:00
Compare commits
35 Commits
docs-artic
...
feature/en
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3389dd3a9c | ||
|
|
d124039984 | ||
|
|
d0aa142cf4 | ||
|
|
eed5299f84 | ||
|
|
fd225a8902 | ||
|
|
8fe1261d2b | ||
|
|
8ce1f08ba4 | ||
|
|
6e65bb459a | ||
|
|
4c33058bae | ||
|
|
bbe0eaa62c | ||
|
|
94aa2121e8 | ||
|
|
9af176667d | ||
|
|
58f4060c76 | ||
|
|
6dde43b5e7 | ||
|
|
8fe1c6b8f7 | ||
|
|
cca659365a | ||
|
|
84f9a27971 | ||
|
|
669477e22c | ||
|
|
2d1ca10dda | ||
|
|
2843f442da | ||
|
|
9c7196d065 | ||
|
|
f8e9bfd62b | ||
|
|
9d960bd6c5 | ||
|
|
3b3019fb67 | ||
|
|
af8720bc86 | ||
|
|
ecdea28021 | ||
|
|
e0ee9be080 | ||
|
|
41bf228bb2 | ||
|
|
b8d60bb716 | ||
|
|
6db36e244c | ||
|
|
abfd742a0f | ||
|
|
937e3654f3 | ||
|
|
bcbe6d98cc | ||
|
|
c00ecdde57 | ||
|
|
ef5174fef3 |
@@ -462,7 +462,9 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
|
||||
return true
|
||||
case "/prometheus/metric-relabel-debug", "/metric-relabel-debug":
|
||||
promscrapeMetricRelabelDebugRequests.Inc()
|
||||
promscrape.WriteMetricRelabelDebug(w, r)
|
||||
rwGlobalRelabelConfigs := remotewrite.GetRemoteWriteRelabelConfigString()
|
||||
rwURLRelabelConfigss := remotewrite.GetURLRelabelConfigString()
|
||||
promscrape.WriteMetricRelabelDebug(w, r, rwGlobalRelabelConfigs, rwURLRelabelConfigss)
|
||||
return true
|
||||
case "/prometheus/target-relabel-debug", "/target-relabel-debug":
|
||||
promscrapeTargetRelabelDebugRequests.Inc()
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
@@ -82,6 +83,16 @@ func WriteRelabelConfigData(w io.Writer) {
|
||||
_, _ = w.Write(*p)
|
||||
}
|
||||
|
||||
// GetRemoteWriteRelabelConfigString returns -remoteWrite.relabelConfig contents in string
|
||||
func GetRemoteWriteRelabelConfigString() string {
|
||||
var bb bytesutil.ByteBuffer
|
||||
WriteRelabelConfigData(&bb)
|
||||
if bb.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
return string(bb.B)
|
||||
}
|
||||
|
||||
// WriteURLRelabelConfigData writes -remoteWrite.urlRelabelConfig contents to w
|
||||
func WriteURLRelabelConfigData(w io.Writer) {
|
||||
p := remoteWriteURLRelabelConfigData.Load()
|
||||
@@ -108,6 +119,24 @@ func WriteURLRelabelConfigData(w io.Writer) {
|
||||
_, _ = w.Write(d)
|
||||
}
|
||||
|
||||
// GetURLRelabelConfigString returns -remoteWrite.urlRelabelConfig contents in []string
|
||||
func GetURLRelabelConfigString() []string {
|
||||
p := remoteWriteURLRelabelConfigData.Load()
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
var ss []string
|
||||
for i := range *remoteWriteURLs {
|
||||
cfgData := (*p)[i]
|
||||
var cfgDataBytes []byte
|
||||
if cfgData != nil {
|
||||
cfgDataBytes, _ = yaml.Marshal(cfgData)
|
||||
}
|
||||
ss = append(ss, string(cfgDataBytes))
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func reloadRelabelConfigs() {
|
||||
rcs := allRelabelConfigs.Load()
|
||||
if !rcs.isSet() {
|
||||
|
||||
@@ -541,7 +541,7 @@ func handleStaticAndSimpleRequests(w http.ResponseWriter, r *http.Request, path
|
||||
return true
|
||||
case "/metric-relabel-debug":
|
||||
promscrapeMetricRelabelDebugRequests.Inc()
|
||||
promscrape.WriteMetricRelabelDebug(w, r)
|
||||
promscrape.WriteMetricRelabelDebug(w, r, "", nil)
|
||||
return true
|
||||
case "/target-relabel-debug":
|
||||
promscrapeTargetRelabelDebugRequests.Inc()
|
||||
|
||||
@@ -31,6 +31,7 @@ See also [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-rel
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): Improve background discovery performance for [http_sd](https://docs.victoriametrics.com/victoriametrics/sd_configs/#http_sd_configs) discovery. See [#8838](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/8838).
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): allow overriding `max_scrape_size` on a per-target basis via the `__max_scrape_size__` label during target relabeling. See [#11188](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11188).
|
||||
* FEATURE: [vmstorage](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): add `-maxBackfillAge` command-line flag for limiting ingestion of samples with historical timestamps, for example, when older data has been moved between storage tiers (nvme/hdd, hot/cold). See [#11199](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11199). Thanks to @AshwinRamaniPsg for contribution.
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): add support for selecting relabel configurations from `-remoteWrite.relabelConfig` and `-remoteWrite.urlRelabelConfig` in the [metrics relabel debug UI](https://docs.victoriametrics.com/victoriametrics/relabeling/#relabel-debugging). See [#9918](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/9918).
|
||||
|
||||
* BUGFIX: `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): Now drops metadata blocks when communicating with vmstorage nodes over the legacy RPC protocol. To avoid this limitation, upgrade `vmstorage` to a version that supports the new RPC protocol (>= [v1.137.0](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/docs/victoriametrics/changelog/CHANGELOG.md#v11370)). See [#11146](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11146).
|
||||
* BUGFIX: [vmbackup](https://docs.victoriametrics.com/victoriametrics/vmbackup/) and [vmbackupmanager](https://docs.victoriametrics.com/victoriametrics/vmbackupmanager/): retry S3 requests failing with `HTTP 429` status code or `TooManyRequests` error code. Previously such requests were not retried, so a short burst of rate limiting would fail the whole backup. See [#11218](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11218). Thanks to @gautamrizwani for contribution.
|
||||
|
||||
@@ -9,47 +9,48 @@ import (
|
||||
)
|
||||
|
||||
// WriteMetricRelabelDebug writes /metric-relabel-debug page to w with the corresponding args.
|
||||
func WriteMetricRelabelDebug(w io.Writer, targetID, metric, relabelConfigs, format string, err error) {
|
||||
writeRelabelDebug(w, false, targetID, metric, relabelConfigs, format, err)
|
||||
func WriteMetricRelabelDebug(w io.Writer, targetID, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, format string, err error) {
|
||||
writeRelabelDebug(w, false, targetID, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, format, err)
|
||||
}
|
||||
|
||||
// WriteTargetRelabelDebug writes /target-relabel-debug page to w with the corresponding args.
|
||||
func WriteTargetRelabelDebug(w io.Writer, targetID, metric, relabelConfigs, format string, err error) {
|
||||
writeRelabelDebug(w, true, targetID, metric, relabelConfigs, format, err)
|
||||
writeRelabelDebug(w, true, targetID, metric, relabelConfigs, 0, 0, format, err)
|
||||
}
|
||||
|
||||
func writeRelabelDebug(w io.Writer, isTargetRelabel bool, targetID, metric, relabelConfigs, format string, err error) {
|
||||
func writeRelabelDebug(w io.Writer, isTargetRelabel bool, targetID, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, format string, err error) {
|
||||
if metric == "" {
|
||||
metric = "{}"
|
||||
}
|
||||
targetURL := ""
|
||||
if err != nil {
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
return
|
||||
}
|
||||
|
||||
metric, err = normalizeInputLabels(metric)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("cannot parse metric: %w", err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
return
|
||||
}
|
||||
|
||||
labels, err := promutil.NewLabelsFromString(metric)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("cannot parse metric: %w", err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
return
|
||||
}
|
||||
|
||||
pcs, err := ParseRelabelConfigsData([]byte(relabelConfigs))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("cannot parse relabel configs: %w", err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
return
|
||||
}
|
||||
|
||||
dss, targetURL := newDebugRelabelSteps(pcs, labels, isTargetRelabel)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, dss, metric, relabelConfigs, nil)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, nil)
|
||||
}
|
||||
|
||||
func newDebugRelabelSteps(pcs *ParsedConfigs, labels *promutil.Labels, isTargetRelabel bool) ([]DebugStep, string) {
|
||||
@@ -140,6 +141,10 @@ func getChangedLabelNames(in, out *promutil.Labels) map[string]struct{} {
|
||||
func normalizeInputLabels(metric string) (string, error) {
|
||||
metric = strings.TrimSpace(metric)
|
||||
|
||||
if strings.ContainsRune(metric, '\n') {
|
||||
return metric, fmt.Errorf("only one time series is allowed; got multiple lines")
|
||||
}
|
||||
|
||||
openBrace := strings.Contains(metric, `{`)
|
||||
closeBrace := strings.Contains(metric, `}`)
|
||||
|
||||
|
||||
@@ -6,37 +6,66 @@
|
||||
|
||||
{% stripspace %}
|
||||
|
||||
{% func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) %}
|
||||
{% func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) %}
|
||||
{% if format == "json" %}
|
||||
{%= RelabelDebugStepsJSON(targetURL, targetID, dss, metric, relabelConfigs, err) %}
|
||||
{%= RelabelDebugStepsJSON(targetURL, targetID, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err) %}
|
||||
{% else %}
|
||||
{%= RelabelDebugStepsHTML(targetURL, targetID, dss, metric, relabelConfigs, err) %}
|
||||
{%= RelabelDebugStepsHTML(targetURL, targetID, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err) %}
|
||||
{% endif %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) %}
|
||||
{% func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{%= htmlcomponents.CommonHeader() %}
|
||||
<title>Metric relabel debug</title>
|
||||
<script>
|
||||
function submitRelabelDebugForm(e) {
|
||||
var form = e.target;
|
||||
var method = "GET";
|
||||
if (form.elements["relabel_configs"].value.length + form.elements["metric"].value.length > 1000) {
|
||||
method = "POST";
|
||||
}
|
||||
form.method = method;
|
||||
function setRelabelDebugFormMethod(form) {
|
||||
form.method = (form.elements["relabel_configs"].value.length + form.elements["metric"].value.length > 1000) ? "POST" : "GET";
|
||||
}
|
||||
|
||||
function reloadRelabelConfigs(select) {
|
||||
if (!confirm('Reload will discard all modifications to the current configuration. Continue?')) {
|
||||
select.value = select.prevValue;
|
||||
return;
|
||||
}
|
||||
document.getElementById('reload_url_relabel_configs').value = '1';
|
||||
setRelabelDebugFormMethod(select.form);
|
||||
select.form.submit();
|
||||
}
|
||||
|
||||
function initRelabelConfigsHighlight() {
|
||||
var ta = document.getElementById('relabel-configs-input');
|
||||
var bd = document.getElementById('relabel-configs-backdrop');
|
||||
if (!ta || !bd) return;
|
||||
function escapeHtml(s) {
|
||||
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
function highlight(text) {
|
||||
return text.split('\n').map(function(line) {
|
||||
var e = escapeHtml(line);
|
||||
return /^\s*#/.test(line)
|
||||
? '<span style="color:#999">'+e+'</span>'
|
||||
: '<span style="color:#212529">'+e+'</span>';
|
||||
}).join('\n');
|
||||
}
|
||||
function update() { bd.innerHTML = highlight(ta.value)+'\n'; }
|
||||
ta.addEventListener('input', update);
|
||||
ta.addEventListener('scroll', function() { bd.scrollTop = ta.scrollTop; });
|
||||
update();
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', initRelabelConfigsHighlight);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{%= htmlcomponents.Navbar() %}
|
||||
<div class="container-fluid">
|
||||
<a href="https://docs.victoriametrics.com/victoriametrics/relabeling/" target="_blank">Relabeling docs</a>{% space %}
|
||||
<a href="https://docs.victoriametrics.com/victoriametrics/relabeling/" target="_blank">Relabeling Cookbook</a>{% space %}|{% space %}
|
||||
<a href="https://docs.victoriametrics.com/victoriametrics/relabeling/#relabeling-stages" target="_blank">Relabeling Stages</a>
|
||||
|
||||
{% if targetID != "" %}
|
||||
{% space %}|{% space %}
|
||||
{% if targetURL != "" %}
|
||||
<a href="metric-relabel-debug?id={%s targetID %}">Metric relabel debug</a>
|
||||
{% else %}
|
||||
@@ -50,14 +79,14 @@ function submitRelabelDebugForm(e) {
|
||||
{% endif %}
|
||||
|
||||
<div class="m-3">
|
||||
<form method="POST" onsubmit="submitRelabelDebugForm(event)">
|
||||
{%= relabelDebugFormInputs(metric, relabelConfigs) %}
|
||||
<form method="POST" onsubmit="setRelabelDebugFormMethod(this)">
|
||||
{%= relabelDebugFormInputs(metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, targetID) %}
|
||||
{% if targetID != "" %}
|
||||
<input type="hidden" name="id" value="{%s targetID %}" />
|
||||
{% endif %}
|
||||
<input type="submit" value="Submit" class="btn btn-primary m-1" />
|
||||
{% if targetID != "" %}
|
||||
<button type="button" onclick="location.href='?id={%s targetID %}'" class="btn btn-secondary m-1">Reset</button>
|
||||
<button type="button" onclick="location.href='?id={%s targetID %}'" class="btn btn-secondary m-1">Reset All</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
@@ -72,15 +101,44 @@ function submitRelabelDebugForm(e) {
|
||||
</html>
|
||||
{% endfunc %}
|
||||
|
||||
{% func relabelDebugFormInputs(metric, relabelConfigs string) %}
|
||||
{% func relabelDebugFormInputs(metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, targetID string) %}
|
||||
<div>
|
||||
Relabel configs:<br/>
|
||||
<textarea name="relabel_configs" style="width: 100%; height: 15em; font-family: monospace" class="m-1">{%s relabelConfigs %}</textarea>
|
||||
</div>
|
||||
<!-- show remote write relabel reload only for scrape metric relabel debug and pure relabel debug. discovery debug should not display this section -->
|
||||
{% if !isTargetRelabel %}
|
||||
<div>
|
||||
<div class="m-1">
|
||||
<div class="d-flex align-items-center gap-2 mt-1">
|
||||
Configs:
|
||||
{% if urlRelabelIndexLength > 0 %}
|
||||
<input type="hidden" name="reload_url_relabel_configs" id="reload_url_relabel_configs" value="" />
|
||||
<select name="url_relabel_configs_index" class="form-select form-select-sm w-auto" onfocus="this.prevValue=this.value" onchange="reloadRelabelConfigs(this)">
|
||||
{% for i := range urlRelabelIndexLength %}
|
||||
{% if urlRelabelIndexCurrent == i %}
|
||||
<option value="{%d i %}" selected="selected">remote-write-url-{%d i %}</option>
|
||||
{% else %}
|
||||
<option value="{%d i %}">remote-write-url-{%d i %}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
Configs:
|
||||
{% endif %}
|
||||
|
||||
<div>
|
||||
Labels:<br/>
|
||||
<textarea name="metric" style="width: 100%; height: 5em; font-family: monospace" class="m-1">{%s metric %}</textarea>
|
||||
<!-- the following text area css was generated with the help of AI to display yaml comments (starting with #) in gray. it could be rewritten in the future -->
|
||||
<div class="m-1" style="position:relative;height:15em;">
|
||||
<div id="relabel-configs-backdrop" style="position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;overflow:hidden;font-family:monospace;white-space:pre-wrap;padding:0.375rem 0.75rem;border:1px solid transparent;"></div>
|
||||
<textarea id="relabel-configs-input" name="relabel_configs" class="form-control" style="position:absolute;top:0;left:0;height:100%;font-family:monospace;color:transparent;caret-color:#212529;background:transparent;resize:none;overflow-y:scroll;">
|
||||
{%s relabelConfigs %}
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
One Time Series:<br/>
|
||||
<textarea name="metric" style="width: 100%; height: 5em; font-family: monospace" class="m-1" placeholder="up{job="job_name",instance="host:port"}">{%s metric %}</textarea>
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
@@ -153,7 +211,7 @@ function submitRelabelDebugForm(e) {
|
||||
{% endif %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) %}
|
||||
{% func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) %}
|
||||
{
|
||||
{% if err != nil %}
|
||||
"status": "error",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,10 +10,10 @@ import (
|
||||
|
||||
// TestWriteRelabelDebugSupportFormats verifies the relabeling debug input, rules and output.
|
||||
func TestWriteRelabelDebugSupportFormats(t *testing.T) {
|
||||
f := func(input, rule, expect string) {
|
||||
f := func(input, rules, expect string) {
|
||||
// execute
|
||||
outputWriter := bytes.NewBuffer(nil)
|
||||
writeRelabelDebug(outputWriter, false, "", input, rule, "json", nil)
|
||||
writeRelabelDebug(outputWriter, false, "", input, rules, 0, 0, "json", nil)
|
||||
|
||||
// the response is in JSON with HTML content, extract the `resultingLabels` in JSON and unescape it.
|
||||
resultingLabels := fastjson.GetString(outputWriter.Bytes(), `resultingLabels`)
|
||||
@@ -41,4 +41,20 @@ func TestWriteRelabelDebugSupportFormats(t *testing.T) {
|
||||
f(`{_name__="metric_name"`, ruleTestParsing, ``)
|
||||
f(`_name__="metric_name}"`, ruleTestParsing, ``)
|
||||
f(`metrics_name}"`, ruleTestParsing, ``)
|
||||
|
||||
// test multiple rules including remote writes
|
||||
// drop all labels and add one in URL relabeling
|
||||
rule1 := `
|
||||
- action: labeldrop
|
||||
regex: "drop_me_metrics_relabel"
|
||||
`
|
||||
rule2 := `
|
||||
- action: labeldrop
|
||||
regex: "drop_me_remote_write_relabel"
|
||||
`
|
||||
rule3 := `
|
||||
- target_label: add_me_url_relabel
|
||||
replacement: added
|
||||
`
|
||||
f(`{__name__="metric_name", drop_me_metrics_relabel="1", drop_me_remote_write_relabel="2"}`, rule1+rule2+rule3, `metric_name{add_me_url_relabel="added"}`)
|
||||
}
|
||||
|
||||
@@ -3,34 +3,103 @@ package promscrape
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutil"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
|
||||
)
|
||||
|
||||
// WriteMetricRelabelDebug serves requests to /metric-relabel-debug page
|
||||
func WriteMetricRelabelDebug(w http.ResponseWriter, r *http.Request) {
|
||||
// WriteMetricRelabelDebug serves requests to /metric-relabel-debug page.
|
||||
// remotewrite-related relabel configs could be empty as vmsingle doesn't provide remote write feature.
|
||||
func WriteMetricRelabelDebug(w http.ResponseWriter, r *http.Request, rwGlobalRelabelConfigs string, rwURLRelabelConfigss []string) {
|
||||
targetID := r.FormValue("id")
|
||||
metric := r.FormValue("metric")
|
||||
relabelConfigs := r.FormValue("relabel_configs")
|
||||
|
||||
// if set, it means user selected another URL from the dropdown and everything will be reloaded.
|
||||
reloadRWURLRelabelConfigs := r.FormValue("reload_url_relabel_configs")
|
||||
rwURLRelabelConfigsIdxStr := r.FormValue("url_relabel_configs_index")
|
||||
|
||||
format := r.FormValue("format")
|
||||
var err error
|
||||
|
||||
if metric == "" && relabelConfigs == "" && targetID != "" {
|
||||
pcs, labels, ok := getMetricRelabelContextByTargetID(targetID)
|
||||
if !ok {
|
||||
err = fmt.Errorf("cannot find target for id=%s", targetID)
|
||||
targetID = ""
|
||||
} else {
|
||||
metric = labels.String()
|
||||
relabelConfigs = pcs.String()
|
||||
// if all per-URL config is empty, it means no per-URL rule is configured.
|
||||
// set it to 0 so the user do not see the options in debug page.
|
||||
rwURLRelabelConfigsLength := 0
|
||||
for _, urlRelabelConfig := range rwURLRelabelConfigss {
|
||||
if urlRelabelConfig != "" {
|
||||
rwURLRelabelConfigsLength = len(rwURLRelabelConfigss)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
rwURLRelabelConfigsIdx, idxErr := strconv.Atoi(rwURLRelabelConfigsIdxStr)
|
||||
if idxErr != nil {
|
||||
rwURLRelabelConfigsIdx = -1
|
||||
}
|
||||
|
||||
// load the initial data with specific remote write URL index (default 0) in 2 cases:
|
||||
// - relabel config is empty. load scrape relabel (if targetID exist) + remote write related relabel (always).
|
||||
// - `reload` is set. load scrape relabel (if targetID exist) + reload remote write related relabel (by the URL index).
|
||||
init := metric == "" && relabelConfigs == "" && reloadRWURLRelabelConfigs == ""
|
||||
reload := reloadRWURLRelabelConfigs != ""
|
||||
if init || reload {
|
||||
// scrape related relabel labels & rules
|
||||
var (
|
||||
pcs = &promrelabel.ParsedConfigs{} // could be empty
|
||||
labels *promutil.Labels
|
||||
ok bool
|
||||
)
|
||||
if targetID != "" {
|
||||
pcs, labels, ok = getMetricRelabelContextByTargetID(targetID)
|
||||
if !ok {
|
||||
err = fmt.Errorf("cannot find target for id=%s", targetID)
|
||||
targetID = ""
|
||||
} else {
|
||||
metric = "up"
|
||||
metric += labels.String()
|
||||
}
|
||||
}
|
||||
|
||||
// general relabel rules (remote write)
|
||||
// set the per-URL remote write relabel according to index, any error will fall back the index to 0.
|
||||
rwURLRelabelConfigs := ""
|
||||
if len(rwURLRelabelConfigss) > 0 {
|
||||
// ignore the error if the input is invalid or exceed the length, and fallback to 0.
|
||||
if rwURLRelabelConfigsIdx < 0 || rwURLRelabelConfigsIdx >= len(rwURLRelabelConfigss) {
|
||||
rwURLRelabelConfigsIdx = 0
|
||||
}
|
||||
rwURLRelabelConfigs = rwURLRelabelConfigss[rwURLRelabelConfigsIdx]
|
||||
}
|
||||
|
||||
relabelConfigs = composeRelabelConfigs(pcs.String(), rwGlobalRelabelConfigs, rwURLRelabelConfigs, rwURLRelabelConfigsIdx)
|
||||
}
|
||||
|
||||
if format == "json" {
|
||||
httpserver.EnableCORS(w, r)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
promrelabel.WriteMetricRelabelDebug(w, targetID, metric, relabelConfigs, format, err)
|
||||
promrelabel.WriteMetricRelabelDebug(w, targetID, metric, relabelConfigs, rwURLRelabelConfigsLength, rwURLRelabelConfigsIdx, format, err)
|
||||
}
|
||||
|
||||
func composeRelabelConfigs(relabelConfigs, rwGlobalRelabelConfigs, rwURLRelabelConfigs string, rwURLIdx int) string {
|
||||
if relabelConfigs != "" {
|
||||
relabelConfigs = "# -promscrape.config .scrape_configs[].metric_relabel_configs\n" + relabelConfigs
|
||||
}
|
||||
|
||||
if rwGlobalRelabelConfigs != "" {
|
||||
relabelConfigs += "\n# -remoteWrite.relabelConfig"
|
||||
relabelConfigs += "\n" + rwGlobalRelabelConfigs
|
||||
}
|
||||
|
||||
if rwURLRelabelConfigs != "" {
|
||||
relabelConfigs += fmt.Sprintf("\n# -remoteWrite.urlRelabelConfig=remote-write-url-%d", rwURLIdx)
|
||||
relabelConfigs += "\n" + rwURLRelabelConfigs
|
||||
}
|
||||
|
||||
return relabelConfigs
|
||||
}
|
||||
|
||||
// WriteTargetRelabelDebug generates response for /target-relabel-debug page
|
||||
@@ -48,7 +117,7 @@ func WriteTargetRelabelDebug(w http.ResponseWriter, r *http.Request) {
|
||||
targetID = ""
|
||||
} else {
|
||||
metric = labels.labelsString()
|
||||
relabelConfigs = pcs.String()
|
||||
relabelConfigs = "# -promscrape.config .scrape_configs[].relabel_configs\n" + pcs.String()
|
||||
}
|
||||
}
|
||||
if format == "json" {
|
||||
|
||||
@@ -11,9 +11,7 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/atomicutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
@@ -40,14 +38,6 @@ const maxInmemoryParts = 60
|
||||
// See appendPartsToMerge tests for details.
|
||||
const defaultPartsToMerge = 15
|
||||
|
||||
// The number of shards for rawRow entries per partition.
|
||||
//
|
||||
// Higher number of shards reduces CPU contention and increases the max bandwidth on multi-core systems.
|
||||
var rawRowsShardsPerPartition = cgroup.AvailableCPUs()
|
||||
|
||||
// The interval for flushing buffered rows into parts, so they become visible to search.
|
||||
const pendingRowsFlushInterval = 2 * time.Second
|
||||
|
||||
// The interval for guaranteed flush of recently ingested data from memory to on-disk parts, so they survive process crash.
|
||||
var dataFlushInterval = 5 * time.Second
|
||||
|
||||
@@ -66,11 +56,6 @@ func SetDataFlushInterval(d time.Duration) {
|
||||
dataFlushInterval = d
|
||||
}
|
||||
|
||||
// The maximum number of rawRow items in rawRowsShard.
|
||||
//
|
||||
// Limit the maximum shard size to 8Mb, since this gives the lowest CPU usage under high ingestion rate.
|
||||
const maxRawRowsPerShard = (8 << 20) / int(unsafe.Sizeof(rawRow{}))
|
||||
|
||||
// partition represents a partition.
|
||||
type partition struct {
|
||||
activeInmemoryMerges atomic.Int64
|
||||
@@ -480,130 +465,11 @@ func (pt *partition) AddRows(rows []rawRow) {
|
||||
}
|
||||
}
|
||||
|
||||
pt.rawRows.addRows(pt, rows)
|
||||
pt.rawRows.addRows(pt.flushRowssToInmemoryParts, rows)
|
||||
}
|
||||
|
||||
var isDebug = false
|
||||
|
||||
type rawRowsShards struct {
|
||||
flushDeadlineMs atomic.Int64
|
||||
|
||||
shardIdx atomic.Uint32
|
||||
|
||||
// Shards reduce lock contention when adding rows on multi-CPU systems.
|
||||
shards []rawRowsShard
|
||||
|
||||
rowssToFlushLock sync.Mutex
|
||||
rowssToFlush [][]rawRow
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) init() {
|
||||
rrss.shards = make([]rawRowsShard, rawRowsShardsPerPartition)
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) addRows(pt *partition, rows []rawRow) {
|
||||
shards := rrss.shards
|
||||
shardsLen := uint32(len(shards))
|
||||
for len(rows) > 0 {
|
||||
n := rrss.shardIdx.Add(1)
|
||||
idx := n % shardsLen
|
||||
tailRows, rowsToFlush := shards[idx].addRows(rows)
|
||||
rrss.addRowsToFlush(pt, rowsToFlush)
|
||||
rows = tailRows
|
||||
}
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) addRowsToFlush(pt *partition, rowsToFlush []rawRow) {
|
||||
if len(rowsToFlush) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var rowssToMerge [][]rawRow
|
||||
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
if len(rrss.rowssToFlush) == 0 {
|
||||
rrss.updateFlushDeadline()
|
||||
}
|
||||
rrss.rowssToFlush = append(rrss.rowssToFlush, rowsToFlush)
|
||||
if len(rrss.rowssToFlush) >= defaultPartsToMerge {
|
||||
rowssToMerge = rrss.rowssToFlush
|
||||
rrss.rowssToFlush = nil
|
||||
}
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
|
||||
pt.flushRowssToInmemoryParts(rowssToMerge)
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) Len() int {
|
||||
n := 0
|
||||
for i := range rrss.shards[:] {
|
||||
n += rrss.shards[i].Len()
|
||||
}
|
||||
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
for _, rows := range rrss.rowssToFlush {
|
||||
n += len(rows)
|
||||
}
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) updateFlushDeadline() {
|
||||
rrss.flushDeadlineMs.Store(time.Now().Add(pendingRowsFlushInterval).UnixMilli())
|
||||
}
|
||||
|
||||
type rawRowsShardNopad struct {
|
||||
flushDeadlineMs atomic.Int64
|
||||
|
||||
mu sync.Mutex
|
||||
rows []rawRow
|
||||
}
|
||||
|
||||
type rawRowsShard struct {
|
||||
rawRowsShardNopad
|
||||
|
||||
// The padding prevents false sharing
|
||||
_ [atomicutil.CacheLineSize - unsafe.Sizeof(rawRowsShardNopad{})%atomicutil.CacheLineSize]byte
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) Len() int {
|
||||
rrs.mu.Lock()
|
||||
n := len(rrs.rows)
|
||||
rrs.mu.Unlock()
|
||||
return n
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) addRows(rows []rawRow) ([]rawRow, []rawRow) {
|
||||
var rowsToFlush []rawRow
|
||||
|
||||
rrs.mu.Lock()
|
||||
if cap(rrs.rows) == 0 {
|
||||
rrs.rows = newRawRows()
|
||||
}
|
||||
if len(rrs.rows) == 0 {
|
||||
rrs.updateFlushDeadline()
|
||||
}
|
||||
n := copy(rrs.rows[len(rrs.rows):cap(rrs.rows)], rows)
|
||||
rrs.rows = rrs.rows[:len(rrs.rows)+n]
|
||||
rows = rows[n:]
|
||||
if len(rows) > 0 {
|
||||
rowsToFlush = rrs.rows
|
||||
rrs.rows = newRawRows()
|
||||
rrs.updateFlushDeadline()
|
||||
n = copy(rrs.rows[:cap(rrs.rows)], rows)
|
||||
rrs.rows = rrs.rows[:n]
|
||||
rows = rows[n:]
|
||||
}
|
||||
rrs.mu.Unlock()
|
||||
|
||||
return rows, rowsToFlush
|
||||
}
|
||||
|
||||
func newRawRows() []rawRow {
|
||||
return make([]rawRow, 0, maxRawRowsPerShard)
|
||||
}
|
||||
|
||||
func (pt *partition) flushRowssToInmemoryParts(rowss [][]rawRow) {
|
||||
if len(rowss) == 0 {
|
||||
return
|
||||
@@ -1142,7 +1008,7 @@ func (pt *partition) pendingRowsFlusher() {
|
||||
}
|
||||
|
||||
func (pt *partition) flushPendingRows(isFinal bool) {
|
||||
pt.rawRows.flush(pt, isFinal)
|
||||
pt.rawRows.flush(pt.flushRowssToInmemoryParts, isFinal)
|
||||
}
|
||||
|
||||
func (pt *partition) flushInmemoryRowsToFiles() {
|
||||
@@ -1168,66 +1034,6 @@ func (pt *partition) flushInmemoryPartsToFiles(isFinal bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) flush(pt *partition, isFinal bool) {
|
||||
var dst [][]rawRow
|
||||
|
||||
currentTimeMs := time.Now().UnixMilli()
|
||||
flushDeadlineMs := rrss.flushDeadlineMs.Load()
|
||||
if isFinal || currentTimeMs >= flushDeadlineMs {
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
dst = rrss.rowssToFlush
|
||||
rrss.rowssToFlush = nil
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
}
|
||||
|
||||
for i := range rrss.shards {
|
||||
dst = rrss.shards[i].appendRawRowsToFlush(dst, currentTimeMs, isFinal)
|
||||
}
|
||||
|
||||
pt.flushRowssToInmemoryParts(dst)
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) appendRawRowsToFlush(dst [][]rawRow, currentTimeMs int64, isFinal bool) [][]rawRow {
|
||||
flushDeadlineMs := rrs.flushDeadlineMs.Load()
|
||||
if !isFinal && currentTimeMs < flushDeadlineMs {
|
||||
// Fast path - nothing to flush
|
||||
return dst
|
||||
}
|
||||
|
||||
// Slow path - move rrs.rows to dst.
|
||||
rrs.mu.Lock()
|
||||
dst = appendRawRowss(dst, rrs.rows)
|
||||
rrs.rows = rrs.rows[:0]
|
||||
rrs.mu.Unlock()
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) updateFlushDeadline() {
|
||||
rrs.flushDeadlineMs.Store(time.Now().Add(pendingRowsFlushInterval).UnixMilli())
|
||||
}
|
||||
|
||||
func appendRawRowss(dst [][]rawRow, src []rawRow) [][]rawRow {
|
||||
if len(src) == 0 {
|
||||
return dst
|
||||
}
|
||||
if len(dst) == 0 {
|
||||
dst = append(dst, newRawRows())
|
||||
}
|
||||
prows := &dst[len(dst)-1]
|
||||
n := copy((*prows)[len(*prows):cap(*prows)], src)
|
||||
*prows = (*prows)[:len(*prows)+n]
|
||||
src = src[n:]
|
||||
for len(src) > 0 {
|
||||
rows := newRawRows()
|
||||
n := copy(rows[:cap(rows)], src)
|
||||
rows = rows[:len(rows)+n]
|
||||
src = src[n:]
|
||||
dst = append(dst, rows)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func (pt *partition) mergePartsToFiles(pws []*partWrapper, stopCh <-chan struct{}, concurrencyCh chan struct{}, useSparseCache bool) error {
|
||||
pwsLen := len(pws)
|
||||
|
||||
|
||||
@@ -3,11 +3,29 @@ package storage
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/atomicutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/decimal"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
)
|
||||
|
||||
// The number of shards for rawRow entries.
|
||||
//
|
||||
// Higher number of shards reduces CPU contention and increases the max bandwidth on multi-core systems.
|
||||
var numRawRowsShards = cgroup.AvailableCPUs()
|
||||
|
||||
// The interval for flushing buffered rows into parts, so they become visible to search.
|
||||
const pendingRowsFlushInterval = 2 * time.Second
|
||||
|
||||
// The maximum number of rawRow items in rawRowsShard.
|
||||
//
|
||||
// Limit the maximum shard size to 8Mb, since this gives the lowest CPU usage under high ingestion rate.
|
||||
const maxRawRowsPerShard = (8 << 20) / int(unsafe.Sizeof(rawRow{}))
|
||||
|
||||
// rawRow represents raw timeseries row.
|
||||
type rawRow struct {
|
||||
// TSID is time series id.
|
||||
@@ -149,3 +167,182 @@ func putRawRowsMarshaler(rrm *rawRowsMarshaler) {
|
||||
}
|
||||
|
||||
var rrmPool sync.Pool
|
||||
|
||||
type rawRowsShards struct {
|
||||
flushDeadlineMs atomic.Int64
|
||||
|
||||
shardIdx atomic.Uint32
|
||||
|
||||
// Shards reduce lock contention when adding rows on multi-CPU systems.
|
||||
shards []rawRowsShard
|
||||
|
||||
rowssToFlushLock sync.Mutex
|
||||
rowssToFlush [][]rawRow
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) init() {
|
||||
rrss.shards = make([]rawRowsShard, numRawRowsShards)
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) Len() int {
|
||||
n := 0
|
||||
for i := range rrss.shards[:] {
|
||||
n += rrss.shards[i].Len()
|
||||
}
|
||||
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
for _, rows := range rrss.rowssToFlush {
|
||||
n += len(rows)
|
||||
}
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) addRows(flush func([][]rawRow), rows []rawRow) {
|
||||
shards := rrss.shards
|
||||
shardsLen := uint32(len(shards))
|
||||
for len(rows) > 0 {
|
||||
n := rrss.shardIdx.Add(1)
|
||||
idx := n % shardsLen
|
||||
tailRows, rowsToFlush := shards[idx].addRows(rows)
|
||||
rrss.addRowsToFlush(flush, rowsToFlush)
|
||||
rows = tailRows
|
||||
}
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) addRowsToFlush(flush func([][]rawRow), rowsToFlush []rawRow) {
|
||||
if len(rowsToFlush) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var rowssToMerge [][]rawRow
|
||||
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
if len(rrss.rowssToFlush) == 0 {
|
||||
rrss.updateFlushDeadline()
|
||||
}
|
||||
rrss.rowssToFlush = append(rrss.rowssToFlush, rowsToFlush)
|
||||
if len(rrss.rowssToFlush) >= defaultPartsToMerge {
|
||||
rowssToMerge = rrss.rowssToFlush
|
||||
rrss.rowssToFlush = nil
|
||||
}
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
|
||||
flush(rowssToMerge)
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) updateFlushDeadline() {
|
||||
rrss.flushDeadlineMs.Store(time.Now().Add(pendingRowsFlushInterval).UnixMilli())
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) flush(flush func(rrs [][]rawRow), isFinal bool) {
|
||||
var dst [][]rawRow
|
||||
|
||||
currentTimeMs := time.Now().UnixMilli()
|
||||
flushDeadlineMs := rrss.flushDeadlineMs.Load()
|
||||
if isFinal || currentTimeMs >= flushDeadlineMs {
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
dst = rrss.rowssToFlush
|
||||
rrss.rowssToFlush = nil
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
}
|
||||
|
||||
for i := range rrss.shards {
|
||||
dst = rrss.shards[i].appendRawRowsToFlush(dst, currentTimeMs, isFinal)
|
||||
}
|
||||
|
||||
flush(dst)
|
||||
}
|
||||
|
||||
type rawRowsShardNopad struct {
|
||||
flushDeadlineMs atomic.Int64
|
||||
|
||||
mu sync.Mutex
|
||||
rows []rawRow
|
||||
}
|
||||
|
||||
type rawRowsShard struct {
|
||||
rawRowsShardNopad
|
||||
|
||||
// The padding prevents false sharing
|
||||
_ [atomicutil.CacheLineSize - unsafe.Sizeof(rawRowsShardNopad{})%atomicutil.CacheLineSize]byte
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) Len() int {
|
||||
rrs.mu.Lock()
|
||||
n := len(rrs.rows)
|
||||
rrs.mu.Unlock()
|
||||
return n
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) addRows(rows []rawRow) ([]rawRow, []rawRow) {
|
||||
var rowsToFlush []rawRow
|
||||
|
||||
rrs.mu.Lock()
|
||||
if cap(rrs.rows) == 0 {
|
||||
rrs.rows = newRawRows()
|
||||
}
|
||||
if len(rrs.rows) == 0 {
|
||||
rrs.updateFlushDeadline()
|
||||
}
|
||||
n := copy(rrs.rows[len(rrs.rows):cap(rrs.rows)], rows)
|
||||
rrs.rows = rrs.rows[:len(rrs.rows)+n]
|
||||
rows = rows[n:]
|
||||
if len(rows) > 0 {
|
||||
rowsToFlush = rrs.rows
|
||||
rrs.rows = newRawRows()
|
||||
rrs.updateFlushDeadline()
|
||||
n = copy(rrs.rows[:cap(rrs.rows)], rows)
|
||||
rrs.rows = rrs.rows[:n]
|
||||
rows = rows[n:]
|
||||
}
|
||||
rrs.mu.Unlock()
|
||||
|
||||
return rows, rowsToFlush
|
||||
}
|
||||
|
||||
func newRawRows() []rawRow {
|
||||
return make([]rawRow, 0, maxRawRowsPerShard)
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) updateFlushDeadline() {
|
||||
rrs.flushDeadlineMs.Store(time.Now().Add(pendingRowsFlushInterval).UnixMilli())
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) appendRawRowsToFlush(dst [][]rawRow, currentTimeMs int64, isFinal bool) [][]rawRow {
|
||||
flushDeadlineMs := rrs.flushDeadlineMs.Load()
|
||||
if !isFinal && currentTimeMs < flushDeadlineMs {
|
||||
// Fast path - nothing to flush
|
||||
return dst
|
||||
}
|
||||
|
||||
// Slow path - move rrs.rows to dst.
|
||||
rrs.mu.Lock()
|
||||
dst = appendRawRowss(dst, rrs.rows)
|
||||
rrs.rows = rrs.rows[:0]
|
||||
rrs.mu.Unlock()
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func appendRawRowss(dst [][]rawRow, src []rawRow) [][]rawRow {
|
||||
if len(src) == 0 {
|
||||
return dst
|
||||
}
|
||||
if len(dst) == 0 {
|
||||
dst = append(dst, newRawRows())
|
||||
}
|
||||
prows := &dst[len(dst)-1]
|
||||
n := copy((*prows)[len(*prows):cap(*prows)], src)
|
||||
*prows = (*prows)[:len(*prows)+n]
|
||||
src = src[n:]
|
||||
for len(src) > 0 {
|
||||
rows := newRawRows()
|
||||
n := copy(rows[:cap(rows)], src)
|
||||
rows = rows[:len(rows)+n]
|
||||
src = src[n:]
|
||||
dst = append(dst, rows)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user