Repository: VictoriaMetrics/VictoriaMetrics
Pull request #5399 (2023-11-27 13:12:08)

docs: vmagent info about p queue disk size


Patch:
diff --git a/Makefile b/Makefile
index ca1b2e920..58469993c 100644
--- a/Makefile
+++ b/Makefile
@@ -174,7 +174,6 @@ victoria-metrics-crossbuild: \
 	victoria-metrics-linux-amd64 \
 	victoria-metrics-linux-arm64 \
 	victoria-metrics-linux-arm \
-	victoria-metrics-linux-386 \
 	victoria-metrics-linux-ppc64le \
 	victoria-metrics-darwin-amd64 \
 	victoria-metrics-darwin-arm64 \
@@ -186,7 +185,6 @@ vmutils-crossbuild: \
 	vmutils-linux-amd64 \
 	vmutils-linux-arm64 \
 	vmutils-linux-arm \
-	vmutils-linux-386 \
 	vmutils-linux-ppc64le \
 	vmutils-darwin-amd64 \
 	vmutils-darwin-arm64 \
diff --git a/README.md b/README.md
index 2651dbcc4..8ac1bfec8 100644
--- a/README.md
+++ b/README.md
@@ -507,10 +507,15 @@ See also [vmagent](https://docs.victoriametrics.com/vmagent.html), which can be
 
 ## How to send data from DataDog agent
 
-VictoriaMetrics accepts data from [DataDog agent](https://docs.datadoghq.com/agent/) 
-or [DogStatsD](https://docs.datadoghq.com/developers/dogstatsd/) 
-via ["submit metrics" API](https://docs.datadoghq.com/api/latest/metrics/#submit-metrics) 
-at `/datadog/api/v1/series` path.
+VictoriaMetrics accepts data in the following protocols:
+* [DataDog agent](https://docs.datadoghq.com/agent/)
+* [DogStatsD](https://docs.datadoghq.com/developers/dogstatsd/)
+* [DataDog Lambda Extension](https://docs.datadoghq.com/serverless/libraries_integrations/extension/)
+
+Via ["submit metrics" API](https://docs.datadoghq.com/api/latest/metrics/#submit-metrics) at the following path:
+* `/datadog/api/v1/series`
+* `/datadog/api/v2/series`
+* `/datadog/api/beta/sketches`
 
 ### Sending metrics to VictoriaMetrics
 
@@ -582,6 +587,19 @@ additional_endpoints:
 
 </div>
 
+### Send via Serverless DataDog plugin
+
+Disable logs (logs ingestion is not supported by Victoria Metrics) and set a custom endpoint in serverless.yaml
+```
+custom:
+  datadog:
+    enableDDLogs: false             # Disabled not supported DD logs
+    apiKey: fakekey                 # Set any key, otherwise plugin fails
+provider:
+  environment:
+    DD_DD_URL: <<vm-url>>/datadog   # Victoria Metrics endpoint for DataDog
+```
+
 ### Send via cURL
 
 See how to send data to VictoriaMetrics via 
@@ -2043,6 +2061,8 @@ Alternatively, single-node VictoriaMetrics can self-scrape the metrics when `-se
 set to duration greater than 0. For example, `-selfScrapeInterval=10s` would enable self-scraping of `/metrics` page 
 with 10 seconds interval.
 
+_Please note, never use loadbalancer address for scraping metrics. All monitored components should be scraped directly by their address._
+
 Official Grafana dashboards available for [single-node](https://grafana.com/grafana/dashboards/10229-victoriametrics/) 
 and [clustered](https://grafana.com/grafana/dashboards/11176-victoriametrics-cluster/) VictoriaMetrics. 
 See an [alternative dashboard for clustered VictoriaMetrics](https://grafana.com/grafana/dashboards/11831) 
@@ -2369,7 +2389,7 @@ It is recommended disabling query cache with `-search.disableCache` command-line
 historical data with timestamps from the past, since the cache assumes that the data is written with
 the current timestamps. Query cache can be enabled after the backfilling is complete.
 
-An alternative solution is to query [/internal/resetRollupResultCache](https://docs.victoriametrics.com/url-examples.html#internalresetRollupResultCache) handler after the backfilling is complete. This will reset the query cache, which could contain incomplete data cached during the backfilling.
+An alternative solution is to query [/internal/resetRollupResultCache](https://docs.victoriametrics.com/url-examples.html#internalresetrollupresultcache) handler after the backfilling is complete. This will reset the query cache, which could contain incomplete data cached during the backfilling.
 
 Yet another solution is to increase `-search.cacheTimestampOffset` flag value in order to disable caching
 for data with timestamps close to the current time. Single-node VictoriaMetrics automatically resets response
@@ -2562,7 +2582,7 @@ Pass `-help` to VictoriaMetrics in order to see the list of supported command-li
   -csvTrimTimestamp duration
      Trim timestamps when importing csv data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
   -datadog.maxInsertRequestSize size
-     The maximum size in bytes of a single DataDog POST request to /api/v1/series
+     The maximum size in bytes of a single DataDog POST request to /api/v1/series, /api/v2/series, /api/beta/sketches
      Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 67108864)
   -datadog.sanitizeMetricName
      Sanitize metric names for the ingested DataDog data to comply with DataDog behaviour described at https://docs.datadoghq.com/metrics/custom_metrics/#naming-custom-metrics (default true)
diff --git a/app/vmagent/datadog/request_handler.go b/app/vmagent/datadog/request_handler.go
index 4cdfe1093..80f41ca95 100644
--- a/app/vmagent/datadog/request_handler.go
+++ b/app/vmagent/datadog/request_handler.go
@@ -8,7 +8,6 @@ import (
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
 	parserCommon "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/common"
-	"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog/stream"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/tenantmetrics"
 	"github.com/VictoriaMetrics/metrics"
@@ -20,7 +19,7 @@ var (
 	rowsPerInsert      = metrics.NewHistogram(`vmagent_rows_per_insert{type="datadog"}`)
 )
 
-// InsertHandlerForHTTP processes remote write for DataDog POST /api/v1/series request.
+// InsertHandlerForHTTP processes remote write for DataDog POST /api/v1/series, /api/v2/series, /api/beta/sketches request.
 //
 // See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
 func InsertHandlerForHTTP(at *auth.Token, req *http.Request) error {
@@ -28,66 +27,23 @@ func InsertHandlerForHTTP(at *auth.Token, req *http.Request) error {
 	if err != nil {
 		return err
 	}
-	ce := req.Header.Get("Content-Encoding")
-	return stream.Parse(req.Body, ce, func(series []datadog.Series) error {
-		return insertRows(at, series, extraLabels)
-	})
+	return stream.Parse(
+		req, func(series prompbmarshal.TimeSeries) error {
+			series.Labels = append(series.Labels, extraLabels...)
+			return insertRows(at, series)
+		},
+	)
 }
 
-func insertRows(at *auth.Token, series []datadog.Series, extraLabels []prompbmarshal.Label) error {
+func insertRows(at *auth.Token, series prompbmarshal.TimeSeries) error {
 	ctx := common.GetPushCtx()
 	defer common.PutPushCtx(ctx)
 
-	rowsTotal := 0
-	tssDst := ctx.WriteRequest.Timeseries[:0]
-	labels := ctx.Labels[:0]
-	samples := ctx.Samples[:0]
-	for i := range series {
-		ss := &series[i]
-		rowsTotal += len(ss.Points)
-		labelsLen := len(labels)
-		labels = append(labels, prompbmarshal.Label{
-			Name:  "__name__",
-			Value: ss.Metric,
-		})
-		if ss.Host != "" {
-			labels = append(labels, prompbmarshal.Label{
-				Name:  "host",
-				Value: ss.Host,
-			})
-		}
-		if ss.Device != "" {
-			labels = append(labels, prompbmarshal.Label{
-				Name:  "device",
-				Value: ss.Device,
-			})
-		}
-		for _, tag := range ss.Tags {
-			name, value := datadog.SplitTag(tag)
-			if name == "host" {
-				name = "exported_host"
-			}
-			labels = append(labels, prompbmarshal.Label{
-				Name:  name,
-				Value: value,
-			})
-		}
-		labels = append(labels, extraLabels...)
-		samplesLen := len(samples)
-		for _, pt := range ss.Points {
-			samples = append(samples, prompbmarshal.Sample{
-				Timestamp: pt.Timestamp(),
-				Value:     pt.Value(),
-			})
-		}
-		tssDst = append(tssDst, prompbmarshal.TimeSeries{
-			Labels:  labels[labelsLen:],
-			Samples: samples[samplesLen:],
-		})
-	}
-	ctx.WriteRequest.Timeseries = tssDst
-	ctx.Labels = labels
-	ctx.Samples = samples
+	rowsTotal := len(series.Samples)
+
+	ctx.WriteRequest.Timeseries = []prompbmarshal.TimeSeries{series}
+	ctx.Labels = series.Labels
+	ctx.Samples = series.Samples
 	if !remotewrite.TryPush(at, &ctx.WriteRequest) {
 		return remotewrite.ErrQueueFullHTTPRetry
 	}
diff --git a/app/vmagent/main.go b/app/vmagent/main.go
index 02938db71..1e170f0a4 100644
--- a/app/vmagent/main.go
+++ b/app/vmagent/main.go
@@ -343,9 +343,9 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
 		fmt.Fprintf(w, `{"status":"ok"}`)
 		return true
 	case "/datadog/api/v1/series":
-		datadogWriteRequests.Inc()
+		datadogWriteSeriesV1Requests.Inc()
 		if err := datadog.InsertHandlerForHTTP(nil, r); err != nil {
-			datadogWriteErrors.Inc()
+			datadogWriteSeriesV1Errors.Inc()
 			httpserver.Errorf(w, r, "%s", err)
 			return true
 		}
@@ -354,6 +354,27 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
 		w.WriteHeader(202)
 		fmt.Fprintf(w, `{"status":"ok"}`)
 		return true
+	case "/datadog/api/v2/series":
+		datadogWriteSeriesV2Requests.Inc()
+		if err := datadog.InsertHandlerForHTTP(nil, r); err != nil {
+			datadogWriteSeriesV2Errors.Inc()
+			httpserver.Errorf(w, r, "%s", err)
+			return true
+		}
+		// See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(202)
+		fmt.Fprintf(w, `{"errors":[]}`)
+		return true
+	case "/datadog/api/beta/sketches":
+		datadogWriteSketchesBetaRequests.Inc()
+		if err := datadog.InsertHandlerForHTTP(nil, r); err != nil {
+			datadogWriteSketchesBetaErrors.Inc()
+			httpserver.Errorf(w, r, "%s", err)
+			return true
+		}
+		w.WriteHeader(202)
+		return true
 	case "/datadog/api/v1/validate":
 		datadogValidateRequests.Inc()
 		// See https://docs.datadoghq.com/api/latest/authentication/#validate-api-key
@@ -566,9 +587,9 @@ func processMultitenantRequest(w http.ResponseWriter, r *http.Request, path stri
 		fmt.Fprintf(w, `{"status":"ok"}`)
 		return true
 	case "datadog/api/v1/series":
-		datadogWriteRequests.Inc()
+		datadogWriteSeriesV1Requests.Inc()
 		if err := datadog.InsertHandlerForHTTP(at, r); err != nil {
-			datadogWriteErrors.Inc()
+			datadogWriteSeriesV1Errors.Inc()
 			httpserver.Errorf(w, r, "%s", err)
 			return true
 		}
@@ -576,6 +597,26 @@ func processMultitenantRequest(w http.ResponseWriter, r *http.Request, path stri
 		w.WriteHeader(202)
 		fmt.Fprintf(w, `{"status":"ok"}`)
 		return true
+	case "datadog/api/v2/series":
+		datadogWriteSeriesV2Requests.Inc()
+		if err := datadog.InsertHandlerForHTTP(at, r); err != nil {
+			datadogWriteSeriesV2Errors.Inc()
+			httpserver.Errorf(w, r, "%s", err)
+			return true
+		}
+		// See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
+		w.WriteHeader(202)
+		fmt.Fprintf(w, `{"errors":[]}`)
+		return true
+	case "datadog/api/beta/sketches":
+		datadogWriteSketchesBetaRequests.Inc()
+		if err := datadog.InsertHandlerForHTTP(at, r); err != nil {
+			datadogWriteSketchesBetaErrors.Inc()
+			httpserver.Errorf(w, r, "%s", err)
+			return true
+		}
+		w.WriteHeader(202)
+		return true
 	case "datadog/api/v1/validate":
 		datadogValidateRequests.Inc()
 		// See https://docs.datadoghq.com/api/latest/authentication/#validate-api-key
@@ -626,8 +667,14 @@ var (
 
 	influxQueryRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/influx/query", protocol="influx"}`)
 
-	datadogWriteRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/datadog/api/v1/series", protocol="datadog"}`)
-	datadogWriteErrors   = metrics.NewCounter(`vmagent_http_request_errors_total{path="/datadog/api/v1/series", protocol="datadog"}`)
+	datadogWriteSeriesV1Requests = metrics.NewCounter(`vmagent_http_requests_total{path="/datadog/api/v1/series", protocol="datadog"}`)
+	datadogWriteSeriesV1Errors   = metrics.NewCounter(`vmagent_http_request_errors_total{path="/datadog/api/v1/series", protocol="datadog"}`)
+
+	datadogWriteSeriesV2Requests = metrics.NewCounter(`vmagent_http_requests_total{path="/datadog/api/v2/series", protocol="datadog"}`)
+	datadogWriteSeriesV2Errors   = metrics.NewCounter(`vmagent_http_request_errors_total{path="/datadog/api/v2/series", protocol="datadog"}`)
+
+	datadogWriteSketchesBetaRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/datadog/api/beta/sketches", protocol="datadog"}`)
+	datadogWriteSketchesBetaErrors   = metrics.NewCounter(`vmagent_http_request_errors_total{path="/datadog/api/beta/sketches", protocol="datadog"}`)
 
 	datadogValidateRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/datadog/api/v1/validate", protocol="datadog"}`)
 	datadogCheckRunRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/datadog/api/v1/check_run", protocol="datadog"}`)
diff --git a/app/vmauth/example_config.yml b/app/vmauth/example_config.yml
index 55d9d4d20..e4e426f1f 100644
--- a/app/vmauth/example_config.yml
+++ b/app/vmauth/example_config.yml
@@ -37,14 +37,14 @@ users:
 
   # All the requests to http://vmauth:8427 with the given Basic Auth (username:password)
   # are proxied to http://localhost:8428 with extra_label=team=dev query arg.
-  # For example, http://vmauth:8427/api/v1/query is routed to http://localhost:8428/api/v1/query?extra_label=team=dev
+  # For example, http://vmauth:8427/api/v1/query is proxied to http://localhost:8428/api/v1/query?extra_label=team=dev
 - username: "local-single-node2"
   password: "***"
   url_prefix: "http://localhost:8428?extra_label=team=dev"
 
   # All the requests to http://vmauth:8427 with the given Basic Auth (username:password)
   # are proxied to https://localhost:8428
-  # For example, http://vmauth:8427/api/v1/query is routed to https://localhost/api/v1/query
+  # For example, http://vmauth:8427/api/v1/query is proxied to https://localhost/api/v1/query
   # TLS verification is ignored for https://localhost.
 - username: "local-single-node-with-tls"
   password: "***"
@@ -111,9 +111,9 @@ users:
   - "http://default1:8888/unsupported_url_handler"
   - "http://default2:8888/unsupported_url_handler"
 
-# Requests without Authorization header are routed according to `unauthorized_user` section.
-# Requests are routed in round-robin fashion between `url_prefix` backends.
-# The deny_partial_response query arg is added to all the routed requests.
+# Requests without Authorization header are proxied according to `unauthorized_user` section.
+# Requests are proxied in round-robin fashion between `url_prefix` backends.
+# The deny_partial_response query arg is added to all the proxied requests.
 # The requests are re-tried if url_prefix backends send 500 or 503 response status codes.
 unauthorized_user:
   url_prefix:
diff --git a/app/vmauth/main.go b/app/vmauth/main.go
index d11c8dfc9..0747e07ff 100644
--- a/app/vmauth/main.go
+++ b/app/vmauth/main.go
@@ -195,7 +195,7 @@ func processRequest(w http.ResponseWriter, r *http.Request, ui *UserInfo) {
 		// Don't change path and add request_path query param for default route.
 		if isDefault {
 			query := targetURL.Query()
-			query.Set("request_path", u.Path)
+			query.Set("request_path", u.String())
 			targetURL.RawQuery = query.Encode()
 		} else { // Update path for regular routes.
 			targetURL = mergeURLs(targetURL, u, dropSrcPathPrefixParts)
diff --git a/app/vminsert/datadog/request_handler.go b/app/vminsert/datadog/request_handler.go
index 9717b7ef5..c8dcea745 100644
--- a/app/vminsert/datadog/request_handler.go
+++ b/app/vminsert/datadog/request_handler.go
@@ -7,7 +7,6 @@ import (
 	"github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/relabel"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
 	parserCommon "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/common"
-	parser "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog/stream"
 	"github.com/VictoriaMetrics/metrics"
 )
@@ -17,7 +16,7 @@ var (
 	rowsPerInsert = metrics.NewHistogram(`vm_rows_per_insert{type="datadog"}`)
 )
 
-// InsertHandlerForHTTP processes remote write for DataDog POST /api/v1/series request.
+// InsertHandlerForHTTP processes remote write for DataDog POST /api/v1/series, /api/v2/series, /api/v1/sketches, /api/beta/sketches request.
 //
 // See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
 func InsertHandlerForHTTP(req *http.Request) error {
@@ -25,62 +24,38 @@ func InsertHandlerForHTTP(req *http.Request) error {
 	if err != nil {
 		return err
 	}
-	ce := req.Header.Get("Content-Encoding")
-	return stream.Parse(req.Body, ce, func(series []parser.Series) error {
-		return insertRows(series, extraLabels)
-	})
+	return stream.Parse(
+		req, func(series prompbmarshal.TimeSeries) error {
+			series.Labels = append(series.Labels, extraLabels...)
+			return insertRows(series)
+		},
+	)
 }
 
-func insertRows(series []parser.Series, extraLabels []prompbmarshal.Label) error {
+func insertRows(series prompbmarshal.TimeSeries) error {
 	ctx := common.GetInsertCtx()
 	defer common.PutInsertCtx(ctx)
 
-	rowsLen := 0
-	for i := range series {
-		rowsLen += len(series[i].Points)
-	}
-	ctx.Reset(rowsLen)
-	rowsTotal := 0
 	hasRelabeling := relabel.HasRelabeling()
-	for i := range series {
-		ss := &series[i]
-		rowsTotal += len(ss.Points)
-		ctx.Labels = ctx.Labels[:0]
-		ctx.AddLabel("", ss.Metric)
-		if ss.Host != "" {
-			ctx.AddLabel("host", ss.Host)
-		}
-		if ss.Device != "" {
-			ctx.AddLabel("device", ss.Device)
-		}
-		for _, tag := range ss.Tags {
-			name, value := parser.SplitTag(tag)
-			if name == "host" {
-				name = "exported_host"
-			}
-			ctx.AddLabel(name, value)
-		}
-		for j := range extraLabels {
-			label := &extraLabels[j]
-			ctx.AddLabel(label.Name, label.Value)
-		}
-		if hasRelabeling {
-			ctx.ApplyRelabeling()
-		}
-		if len(ctx.Labels) == 0 {
-			// Skip metric without labels.
-			continue
-		}
-		ctx.SortLabelsIfNeeded()
-		var metricNameRaw []byte
-		var err error
-		for _, pt := range ss.Points {
-			timestamp := pt.Timestamp()
-			value := pt.Value()
-			metricNameRaw, err = ctx.WriteDataPointExt(metricNameRaw, ctx.Labels, timestamp, value)
-			if err != nil {
-				return err
-			}
+	rowsTotal := len(series.Samples)
+
+	ctx.Reset(rowsTotal)
+	ctx.Labels = ctx.Labels[:0]
+	for l := range series.Labels {
+		ctx.AddLabel(series.Labels[l].Name, series.Labels[l].Value)
+	}
+	if hasRelabeling {
+		ctx.ApplyRelabeling()
+	}
+	if len(ctx.Labels) == 0 {
+		return nil
+	}
+	ctx.SortLabelsIfNeeded()
+
+	for _, sample := range series.Samples {
+		_, err := ctx.WriteDataPointExt(nil, ctx.Labels, sample.Timestamp, sample.Value)
+		if err != nil {
+			return err
 		}
 	}
 	rowsInserted.Add(rowsTotal)
diff --git a/app/vminsert/main.go b/app/vminsert/main.go
index a53f8216d..43a9ea645 100644
--- a/app/vminsert/main.go
+++ b/app/vminsert/main.go
@@ -246,9 +246,9 @@ func RequestHandler(w http.ResponseWriter, r *http.Request) bool {
 		fmt.Fprintf(w, `{"status":"ok"}`)
 		return true
 	case "/datadog/api/v1/series":
-		datadogWriteRequests.Inc()
+		datadogWriteSeriesV1Requests.Inc()
 		if err := datadog.InsertHandlerForHTTP(r); err != nil {
-			datadogWriteErrors.Inc()
+			datadogWriteSeriesV1Errors.Inc()
 			httpserver.Errorf(w, r, "%s", err)
 			return true
 		}
@@ -257,6 +257,27 @@ func RequestHandler(w http.ResponseWriter, r *http.Request) bool {
 		w.WriteHeader(202)
 		fmt.Fprintf(w, `{"status":"ok"}`)
 		return true
+	case "/datadog/api/v2/series":
+		datadogWriteSeriesV2Requests.Inc()
+		if err := datadog.InsertHandlerForHTTP(r); err != nil {
+			datadogWriteSeriesV2Errors.Inc()
+			httpserver.Errorf(w, r, "%s", err)
+			return true
+		}
+		// See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(202)
+		fmt.Fprintf(w, `{"errors":[]}`)
+		return true
+	case "/datadog/api/beta/sketches":
+		datadogWriteSketchesBetaRequests.Inc()
+		if err := datadog.InsertHandlerForHTTP(r); err != nil {
+			datadogWriteSketchesBetaErrors.Inc()
+			httpserver.Errorf(w, r, "%s", err)
+			return true
+		}
+		w.WriteHeader(202)
+		return true
 	case "/datadog/api/v1/validate":
 		datadogValidateRequests.Inc()
 		// See https://docs.datadoghq.com/api/latest/authentication/#validate-api-key
@@ -371,8 +392,14 @@ var (
 
 	influxQueryRequests = metrics.NewCounter(`vm_http_requests_total{path="/influx/query", protocol="influx"}`)
 
-	datadogWriteRequests = metrics.NewCounter(`vm_http_requests_total{path="/datadog/api/v1/series", protocol="datadog"}`)
-	datadogWriteErrors   = metrics.NewCounter(`vm_http_request_errors_total{path="/datadog/api/v1/series", protocol="datadog"}`)
+	datadogWriteSeriesV1Requests = metrics.NewCounter(`vm_http_requests_total{path="/datadog/api/v1/series", protocol="datadog"}`)
+	datadogWriteSeriesV1Errors   = metrics.NewCounter(`vm_http_request_errors_total{path="/datadog/api/v1/series", protocol="datadog"}`)
+
+	datadogWriteSeriesV2Requests = metrics.NewCounter(`vm_http_requests_total{path="/datadog/api/v2/series", protocol="datadog"}`)
+	datadogWriteSeriesV2Errors   = metrics.NewCounter(`vm_http_request_errors_total{path="/datadog/api/v2/series", protocol="datadog"}`)
+
+	datadogWriteSketchesBetaRequests = metrics.NewCounter(`vm_http_requests_total{path="/datadog/api/beta/sketches", protocol="datadog"}`)
+	datadogWriteSketchesBetaErrors   = metrics.NewCounter(`vm_http_request_errors_total{path="/datadog/api/beta/sketches", protocol="datadog"}`)
 
 	datadogValidateRequests = metrics.NewCounter(`vm_http_requests_total{path="/datadog/api/v1/validate", protocol="datadog"}`)
 	datadogCheckRunRequests = metrics.NewCounter(`vm_http_requests_total{path="/datadog/api/v1/check_run", protocol="datadog"}`)
diff --git a/app/vmselect/promql/transform.go b/app/vmselect/promql/transform.go
index 0088e3044..a52055dbb 100644
--- a/app/vmselect/promql/transform.go
+++ b/app/vmselect/promql/transform.go
@@ -42,6 +42,7 @@ var transformFuncs = map[string]transformFunc{
 	"cosh":                       newTransformFuncOneArg(transformCosh),
 	"day_of_month":               newTransformFuncDateTime(transformDayOfMonth),
 	"day_of_week":                newTransformFuncDateTime(transformDayOfWeek),
+	"day_of_year":                newTransformFuncDateTime(transformDayOfYear),
 	"days_in_month":              newTransformFuncDateTime(transformDaysInMonth),
 	"deg":                        newTransformFuncOneArg(transformDeg),
 	"drop_common_labels":         transformDropCommonLabels,
@@ -353,6 +354,10 @@ func newTransformFuncDateTime(f func(t time.Time) int) transformFunc {
 	}
 }
 
+func transformDayOfYear(t time.Time) int {
+	return t.YearDay()
+}
+
 func transformDayOfMonth(t time.Time) int {
 	return t.Day()
 }
diff --git a/docs/Articles.md b/docs/Articles.md
index b5f68ab36..dbd871e97 100644
--- a/docs/Articles.md
+++ b/docs/Articles.md
@@ -83,6 +83,7 @@ See also [case studies](https://docs.victoriametrics.com/CaseStudies.html).
 * [Solving metrics at scale with VictoriaMetrics](https://sarthak-acoustic.medium.com/solving-metrics-at-scale-with-victoriametrics-ac9c306826c3)
 * [VictoriaMetrics: a comprehensive guide](https://medium.com/@seifeddinerajhi/victoriametrics-a-comprehensive-guide-comparing-it-to-prometheus-and-implementing-kubernetes-03eb8feb0cc2)
 * [Unleashing VM histograms for Ruby: Migrating from Prometheus to VictoriaMetrics with vm-client](https://hackernoon.com/unleashing-vm-histograms-for-ruby-migrating-from-prometheus-to-victoriametrics-with-vm-client)
+* [Observe and record performance of Spark jobs with Victoria Metrics](https://medium.com/constructor-engineering/observe-and-record-performance-of-databricks-jobs-11ffe236555e)
 
 ## Our articles
 
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 8a48a8481..244e7ae68 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -30,10 +30,24 @@ The sandbox cluster installation is running under the constant load generated by
 
 * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): add `-remoteWrite.disableOnDiskQueue` command-line flag, which can be used for disabling data queueing to disk when the remote storage cannot keep up with the data ingestion rate. See [these docs](https://docs.victoriametrics.com/vmagent.html#disabling-on-disk-persistence) and [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2110).
 * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): add support for reading and writing samples via [Google PubSub](https://cloud.google.com/pubsub). See [these docs](https://docs.victoriametrics.com/vmagent.html#google-pubsub-integration).
+* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): add support for Datadog `/api/v2/series` and `/api/beta/sketches` ingestion protocols to vmagent/vminsert components. See this [doc](https://docs.victoriametrics.com/#how-to-send-data-from-datadog-agent) for examples. Thanks to @AndrewChubatiuk for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5094).
 * FEATURE: reduce the default value for `-import.maxLineLen` command-line flag from 100MB to 10MB in order to prevent excessive memory usage during data import via [/api/v1/import](https://docs.victoriametrics.com/#how-to-import-data-in-json-line-format).
+* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): add `keep_if_contains` and `drop_if_contains` relabeling actions. See [these docs](https://docs.victoriametrics.com/vmagent.html#relabeling-enhancements) for details.
+* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add [day_of_year()](https://docs.victoriametrics.com/MetricsQL.html#day_of_year) function, which returns the day of the year for each of the given unix timestamps. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5345) for details. Thanks to @luckyxiaoqiang for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5368/).
+* FEATURE: all VictoriaMetrics binaries: expose additional metrics at `/metrics` page, which may simplify debugging of VictoriaMetrics components (see [this feature request](https://github.com/VictoriaMetrics/metrics/issues/54)):
+  * `go_sched_latencies_seconds` - the [histogram](https://docs.victoriametrics.com/keyConcepts.html#histogram), which shows the time goroutines have spent in runnable state before actually running. Big values point to the lack of CPU time for the current workload.
+  * `go_mutex_wait_seconds_total` - the [counter](https://docs.victoriametrics.com/keyConcepts.html#counter), which shows the total time spent by goroutines waiting for locked mutex. Big values point to mutex contention issues.
+  * `go_gc_cpu_seconds_total` - the [counter](https://docs.victoriametrics.com/keyConcepts.html#counter), which shows the total CPU time spent by Go garbage collector.
+  * `go_gc_mark_assist_cpu_seconds_total` - the [counter](https://docs.victoriametrics.com/keyConcepts.html#counter), which shows the total CPU time spent by goroutines in GC mark assist state.
+  * `go_gc_pauses_seconds` - the [histogram](https://docs.victoriametrics.com/keyConcepts.html#histogram), which shows the duration of GC pauses.
+  * `go_scavenge_cpu_seconds_total` - the [counter](https://docs.victoriametrics.com/keyConcepts.html#counter), which shows the total CPU time spent by Go runtime for returning memory to the Operating System.
+  * `go_memlimit_bytes` - the value of [GOMEMLIMIT](https://pkg.go.dev/runtime#hdr-Environment_Variables) environment variable.
 
 * BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): prevent from `FATAL: cannot flush metainfo` panic when [`-remoteWrite.multitenantURL`](https://docs.victoriametrics.com/vmagent.html#multitenancy) command-line flag is set. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5357).
 * BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly decode zstd-encoded data blocks received via [VictoriaMetrics remote_write protocol](https://docs.victoriametrics.com/vmagent.html#victoriametrics-remote-write-protocol). See [this issue comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5301#issuecomment-1815871992).
+* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly add new labels at `output_relabel_configs` during [stream aggregation](https://docs.victoriametrics.com/stream-aggregation.html). Previously this could lead to corrupted labels in output samples. Thanks to @ChengChung for providing [detailed report for this bug](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5402).
+* BUGFIX: [vmalert-tool](https://docs.victoriametrics.com/#vmalert-tool): allow using arbitrary `eval_time`  in [alert_rule_test](https://docs.victoriametrics.com/vmalert-tool.html#alert_test_case) case. Previously, test cases with `eval_time`  not being a multiple of `evaluation_interval` would fail.
+
 
 ## [v1.95.1](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.95.1)
 
diff --git a/docs/Cluster-VictoriaMetrics.md b/docs/Cluster-VictoriaMetrics.md
index 4d5177fb1..0a4ebaaaa 100644
--- a/docs/Cluster-VictoriaMetrics.md
+++ b/docs/Cluster-VictoriaMetrics.md
@@ -246,7 +246,7 @@ the following approaches for automatic discovery of `vmstorage` nodes:
   The list of discovered `vmstorage` nodes is automatically updated when the file contents changes.
   The update frequency can be controlled with `-storageNode.discoveryInterval` command-line flag.
 
-- [dns+srv](https://en.wikipedia.org/wiki/SRV_record) - pass `dns+src:some-name` value to `-storageNode` command-line flag.
+- [dns+srv](https://en.wikipedia.org/wiki/SRV_record) - pass `dns+srv:some-name` value to `-storageNode` command-line flag.
   In this case the provided `dns+srv` names are resolved into tcp addresses of `vmstorage` nodes.
   The list of discovered `vmstorage` nodes is automatically updated at `vminsert` and `vmselect`
   when it changes behind the corresponding `dns+srv` names.
@@ -363,7 +363,8 @@ Check practical examples of VictoriaMetrics API [here](https://docs.victoriametr
     - `prometheus/api/v1/import/csv` - for importing arbitrary CSV data. See [these docs](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#how-to-import-csv-data) for details.
     - `prometheus/api/v1/import/prometheus` - for importing data in [Prometheus text exposition format](https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md#text-based-format) and in [OpenMetrics format](https://github.com/OpenObservability/OpenMetrics/blob/master/specification/OpenMetrics.md). This endpoint also supports [Pushgateway protocol](https://github.com/prometheus/pushgateway#url). See [these docs](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#how-to-import-data-in-prometheus-exposition-format) for details.
     - `opentelemetry/api/v1/push` - for ingesting data via [OpenTelemetry protocol for metrics](https://github.com/open-telemetry/opentelemetry-specification/blob/ffddc289462dfe0c2041e3ca42a7b1df805706de/specification/metrics/data-model.md). See [these docs](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#sending-data-via-opentelemetry).
-    - `datadog/api/v1/series` - for ingesting data with [DataDog submit metrics API](https://docs.datadoghq.com/api/latest/metrics/#submit-metrics). See [these docs](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#how-to-send-data-from-datadog-agent) for details.
+    - `datadog/api/v1/series` - for ingesting data with [DataDog submit metrics API v1](https://docs.datadoghq.com/api/latest/metrics/#submit-metrics). See [these docs](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#how-to-send-data-from-datadog-agent) for details.
+    - `datadog/api/v2/series` - for ingesting data with [DataDog submit metrics API v2](https://docs.datadoghq.com/api/latest/metrics/#submit-metrics). See [these docs](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#how-to-send-data-from-datadog-agent) for details.
     - `influx/write` and `influx/api/v2/write` - for ingesting data with [InfluxDB line protocol](https://docs.influxdata.com/influxdb/v1.7/write_protocols/line_protocol_tutorial/). See [these docs](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#how-to-send-data-from-influxdb-compatible-agents-such-as-telegraf) for details.
     - `newrelic/infra/v2/metrics/events/bulk` - for accepting data from [NewRelic infrastructure agent](https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent). See [these docs](https://docs.victoriametrics.com/#how-to-send-data-from-newrelic-agent) for details.
     - `opentsdb/api/put` - for accepting [OpenTSDB HTTP /api/put requests](http://opentsdb.net/docs/build/html/api_http/put.html). This handler is disabled by default. It is exposed on a distinct TCP address set via `-opentsdbHTTPListenAddr` command-line flag. See [these docs](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#sending-opentsdb-data-via-http-apiput-requests) for details.
@@ -896,7 +897,7 @@ Below is the output for `/path/to/vminsert -help`:
   -csvTrimTimestamp duration
      Trim timestamps when importing csv data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
   -datadog.maxInsertRequestSize size
-     The maximum size in bytes of a single DataDog POST request to /api/v1/series
+     The maximum size in bytes of a single DataDog POST request to /api/v1/series, /api/v2/series, /api/beta/sketches
      Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 67108864)
   -datadog.sanitizeMetricName
      Sanitize metric names for the ingested DataDog data to comply with DataDog behaviour described at https://docs.datadoghq.com/metrics/custom_metrics/#naming-custom-metrics (default true)
diff --git a/docs/MetricsQL.md b/docs/MetricsQL.md
index 92d713cf0..fa876b9d8 100644
--- a/docs/MetricsQL.md
+++ b/docs/MetricsQL.md
@@ -1049,6 +1049,15 @@ Metric names are stripped from the resulting series. Add [keep_metric_names](#ke
 
 This function is supported by PromQL.
 
+#### day_of_year
+
+`day_of_year(q)` is a [transform function](#transform-functions), which returns the day of year for every point of every time series returned by `q`.
+It is expected that `q` returns unix timestamps. The returned values are in the range `[1...365]` for non-leap years, and `[1 to 366]` in leap years.
+
+Metric names are stripped from the resulting series. Add [keep_metric_names](#keep_metric_names) modifier in order to keep metric names.
+
+This function is supported by PromQL.
+
 #### days_in_month
 
 `days_in_month(q)` is a [transform function](#transform-functions), which returns the number of days in the month identified
diff --git a/docs/README.md b/docs/README.md
index 0f977ee12..16a884c09 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -510,10 +510,15 @@ See also [vmagent](https://docs.victoriametrics.com/vmagent.html), which can be
 
 ## How to send data from DataDog agent
 
-VictoriaMetrics accepts data from [DataDog agent](https://docs.datadoghq.com/agent/) 
-or [DogStatsD](https://docs.datadoghq.com/developers/dogstatsd/) 
-via ["submit metrics" API](https://docs.datadoghq.com/api/latest/metrics/#submit-metrics) 
-at `/datadog/api/v1/series` path.
+VictoriaMetrics accepts data in the following protocols:
+* [DataDog agent](https://docs.datadoghq.com/agent/)
+* [DogStatsD](https://docs.datadoghq.com/developers/dogstatsd/)
+* [DataDog Lambda Extension](https://docs.datadoghq.com/serverless/libraries_integrations/extension/)
+
+Via ["submit metrics" API](https://docs.datadoghq.com/api/latest/metrics/#submit-metrics) at the following path:
+* `/datadog/api/v1/series`
+* `/datadog/api/v2/series`
+* `/datadog/api/beta/sketches`
 
 ### Sending metrics to VictoriaMetrics
 
@@ -585,6 +590,19 @@ additional_endpoints:
 
 </div>
 
+### Send via Serverless DataDog plugin
+
+Disable logs (logs ingestion is not supported by Victoria Metrics) and set a custom endpoint in serverless.yaml
+```
+custom:
+  datadog:
+    enableDDLogs: false             # Disabled not supported DD logs
+    apiKey: fakekey                 # Set any key, otherwise plugin fails
+provider:
+  environment:
+    DD_DD_URL: <<vm-url>>/datadog   # Victoria Metrics endpoint for DataDog
+```
+
 ### Send via cURL
 
 See how to send data to VictoriaMetrics via 
@@ -2046,6 +2064,8 @@ Alternatively, single-node VictoriaMetrics can self-scrape the metrics when `-se
 set to duration greater than 0. For example, `-selfScrapeInterval=10s` would enable self-scraping of `/metrics` page 
 with 10 seconds interval.
 
+_Please note, never use loadbalancer address for scraping metrics. All monitored components should be scraped directly by their address._
+
 Official Grafana dashboards available for [single-node](https://grafana.com/grafana/dashboards/10229-victoriametrics/) 
 and [clustered](https://grafana.com/grafana/dashboards/11176-victoriametrics-cluster/) VictoriaMetrics. 
 See an [alternative dashboard for clustered VictoriaMetrics](https://grafana.com/grafana/dashboards/11831) 
@@ -2372,7 +2392,7 @@ It is recommended disabling query cache with `-search.disableCache` command-line
 historical data with timestamps from the past, since the cache assumes that the data is written with
 the current timestamps. Query cache can be enabled after the backfilling is complete.
 
-An alternative solution is to query [/internal/resetRollupResultCache](https://docs.victoriametrics.com/url-examples.html#internalresetRollupResultCache) handler after the backfilling is complete. This will reset the query cache, which could contain incomplete data cached during the backfilling.
+An alternative solution is to query [/internal/resetRollupResultCache](https://docs.victoriametrics.com/url-examples.html#internalresetrollupresultcache) handler after the backfilling is complete. This will reset the query cache, which could contain incomplete data cached during the backfilling.
 
 Yet another solution is to increase `-search.cacheTimestampOffset` flag value in order to disable caching
 for data with timestamps close to the current time. Single-node VictoriaMetrics automatically resets response
@@ -2565,7 +2585,7 @@ Pass `-help` to VictoriaMetrics in order to see the list of supported command-li
   -csvTrimTimestamp duration
      Trim timestamps when importing csv data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
   -datadog.maxInsertRequestSize size
-     The maximum size in bytes of a single DataDog POST request to /api/v1/series
+     The maximum size in bytes of a single DataDog POST request to /api/v1/series, /api/v2/series, /api/beta/sketches
      Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 67108864)
   -datadog.sanitizeMetricName
      Sanitize metric names for the ingested DataDog data to comply with DataDog behaviour described at https://docs.datadoghq.com/metrics/custom_metrics/#naming-custom-metrics (default true)
diff --git a/docs/Single-server-VictoriaMetrics.md b/docs/Single-server-VictoriaMetrics.md
index 31745d442..d11d9f385 100644
--- a/docs/Single-server-VictoriaMetrics.md
+++ b/docs/Single-server-VictoriaMetrics.md
@@ -518,10 +518,15 @@ See also [vmagent](https://docs.victoriametrics.com/vmagent.html), which can be
 
 ## How to send data from DataDog agent
 
-VictoriaMetrics accepts data from [DataDog agent](https://docs.datadoghq.com/agent/) 
-or [DogStatsD](https://docs.datadoghq.com/developers/dogstatsd/) 
-via ["submit metrics" API](https://docs.datadoghq.com/api/latest/metrics/#submit-metrics) 
-at `/datadog/api/v1/series` path.
+VictoriaMetrics accepts data in the following protocols:
+* [DataDog agent](https://docs.datadoghq.com/agent/)
+* [DogStatsD](https://docs.datadoghq.com/developers/dogstatsd/)
+* [DataDog Lambda Extension](https://docs.datadoghq.com/serverless/libraries_integrations/extension/)
+
+Via ["submit metrics" API](https://docs.datadoghq.com/api/latest/metrics/#submit-metrics) at the following path:
+* `/datadog/api/v1/series`
+* `/datadog/api/v2/series`
+* `/datadog/api/beta/sketches`
 
 ### Sending metrics to VictoriaMetrics
 
@@ -593,6 +598,19 @@ additional_endpoints:
 
 </div>
 
+### Send via Serverless DataDog plugin
+
+Disable logs (logs ingestion is not supported by Victoria Metrics) and set a custom endpoint in serverless.yaml
+```
+custom:
+  datadog:
+    enableDDLogs: false             # Disabled not supported DD logs
+    apiKey: fakekey                 # Set any key, otherwise plugin fails
+provider:
+  environment:
+    DD_DD_URL: <<vm-url>>/datadog   # Victoria Metrics endpoint for DataDog
+```
+
 ### Send via cURL
 
 See how to send data to VictoriaMetrics via 
@@ -2054,6 +2072,8 @@ Alternatively, single-node VictoriaMetrics can self-scrape the metrics when `-se
 set to duration greater than 0. For example, `-selfScrapeInterval=10s` would enable self-scraping of `/metrics` page 
 with 10 seconds interval.
 
+_Please note, never use loadbalancer address for scraping metrics. All monitored components should be scraped directly by their address._
+
 Official Grafana dashboards available for [single-node](https://grafana.com/grafana/dashboards/10229-victoriametrics/) 
 and [clustered](https://grafana.com/grafana/dashboards/11176-victoriametrics-cluster/) VictoriaMetrics. 
 See an [alternative dashboard for clustered VictoriaMetrics](https://grafana.com/grafana/dashboards/11831) 
@@ -2380,7 +2400,7 @@ It is recommended disabling query cache with `-search.disableCache` command-line
 historical data with timestamps from the past, since the cache assumes that the data is written with
 the current timestamps. Query cache can be enabled after the backfilling is complete.
 
-An alternative solution is to query [/internal/resetRollupResultCache](https://docs.victoriametrics.com/url-examples.html#internalresetRollupResultCache) handler after the backfilling is complete. This will reset the query cache, which could contain incomplete data cached during the backfilling.
+An alternative solution is to query [/internal/resetRollupResultCache](https://docs.victoriametrics.com/url-examples.html#internalresetrollupresultcache) handler after the backfilling is complete. This will reset the query cache, which could contain incomplete data cached during the backfilling.
 
 Yet another solution is to increase `-search.cacheTimestampOffset` flag value in order to disable caching
 for data with timestamps close to the current time. Single-node VictoriaMetrics automatically resets response
@@ -2573,7 +2593,7 @@ Pass `-help` to VictoriaMetrics in order to see the list of supported command-li
   -csvTrimTimestamp duration
      Trim timestamps when importing csv data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
   -datadog.maxInsertRequestSize size
-     The maximum size in bytes of a single DataDog POST request to /api/v1/series
+     The maximum size in bytes of a single DataDog POST request to /api/v1/series, /api/v2/series, /api/beta/sketches
      Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 67108864)
   -datadog.sanitizeMetricName
      Sanitize metric names for the ingested DataDog data to comply with DataDog behaviour described at https://docs.datadoghq.com/metrics/custom_metrics/#naming-custom-metrics (default true)
diff --git a/docs/Troubleshooting.md b/docs/Troubleshooting.md
index e682e6d90..f12595164 100644
--- a/docs/Troubleshooting.md
+++ b/docs/Troubleshooting.md
@@ -154,6 +154,8 @@ If you see unexpected or unreliable query results from VictoriaMetrics, then try
    - By passing `nocache=1` query arg to every request to `/api/v1/query` and `/api/v1/query_range`.
      If you use Grafana, then this query arg can be specified in `Custom Query Parameters` field
      at Prometheus datasource settings - see [these docs](https://grafana.com/docs/grafana/latest/datasources/prometheus/) for details.
+    
+    If the problem was in the cache, try resetting it via [resetRollupCache handler](https://docs.victoriametrics.com/url-examples.html#internalresetrollupresultcache).
 
 1. If you use cluster version of VictoriaMetrics, then it may return partial responses by default
    when some of `vmstorage` nodes are temporarily unavailable - see [cluster availability docs](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html#cluster-availability)
diff --git a/docs/operator/resources/vmrule.md b/docs/operator/resources/vmrule.md
index 1dd121bb1..20bfc4ec7 100644
--- a/docs/operator/resources/vmrule.md
+++ b/docs/operator/resources/vmrule.md
@@ -104,10 +104,10 @@ metadata:
   name: vmrule-recording-example
 spec:
   groups:
-    - name: vmalert
+    - name: vmrule_recording_groupname
       interval: 1m
       rules:
-        - alert: vmalert config reload error
+        - record: vm_http_request_errors_total:sum_by_cluster_namespace_job:rate:5m
           expr: |-
             sum by (cluster, namespace, job) (
               rate(vm_http_request_errors_total[5m])
diff --git a/docs/operator/vars.md b/docs/operator/vars.md
index e789effc8..f837664e9 100644
--- a/docs/operator/vars.md
+++ b/docs/operator/vars.md
@@ -10,7 +10,7 @@ menu:
 
 <!-- this doc autogenerated - don't edit it manually -->
 # Auto Generated vars for package config 
- updated at Mon Nov 27 08:54:12 UTC 2023 
+ updated at Tue Nov 28 15:49:38 UTC 2023 
 
 
 | varible name | variable default value | variable required | variable description |
diff --git a/docs/url-examples.md b/docs/url-examples.md
index 93e6396f9..f892c76d9 100644
--- a/docs/url-examples.md
+++ b/docs/url-examples.md
@@ -275,7 +275,7 @@ Additional information:
 * [How to export time series](https://docs.victoriametrics.com/#how-to-export-time-series)
 * [URL format for VictoriaMetrics cluster](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html#url-format)
 
-## /api/v1/labels
+### /api/v1/labels
 
 **Get a list of label names at the given time range**
 
@@ -529,6 +529,85 @@ echo '
 
 </div>
 
+### /datadog/api/v2/series
+
+**Imports data in DataDog format into VictoriaMetrics**
+
+Single-node VictoriaMetrics:
+<div class="with-copy" markdown="1">
+
+```console
+echo '
+{
+  "series": [
+    {
+      "interval": 20,
+      "metric": "system.load.1",
+      "resources": [
+        {
+          "name": "test.example.com",
+          "type": "host"
+        }
+      ],
+      "points": [
+        {
+          "timestamp": 1699152159,
+          "value": 0
+        },
+        {
+          "timestamp": 1699152160,
+          "value": 0.5
+        }
+      ],
+      "tags": [
+        "environment:test"
+      ],
+      "type": "rate"
+    }
+  ]
+}
+' | curl -X POST -H 'Content-Type: application/json' --data-binary @- http://localhost:8428/datadog/api/v2/series
+```
+
+</div>
+
+Cluster version of VictoriaMetrics:
+<div class="with-copy" markdown="1">
+
+```console
+echo '
+{
+  "series": [
+    {
+      "interval": 20,
+      "metric": "system.load.1",
+      "resources": [
+        {
+          "name": "test.example.com",
+          "type": "host"
+        }
+      ],
+      "points": [
+        {
+          "timestamp": 1699152159,
+          "value": 0
+        },
+        {
+          "timestamp": 1699152160,
+          "value": 0.5
+        }
+      ],
+      "tags": [
+        "environment:test"
+      ],
+      "type": "rate"
+    }
+  ]
+}
+' | curl -X POST -H 'Content-Type: application/json' --data-binary @- 'http://<vminsert>:8480/insert/0/datadog/api/v2/series
+
+</div>
+
 Additional information:
 
 * [How to send data from datadog agent](https://docs.victoriametrics.com/#how-to-send-data-from-datadog-agent)
@@ -631,7 +710,7 @@ curl -Is http://localhost:8428/internal/resetRollupResultCache
 
 </div>
 
-Cluster version of VictoriaMetrics::
+Cluster version of VictoriaMetrics:
 
 <div class="with-copy" markdown="1">
 
@@ -639,6 +718,10 @@ Cluster version of VictoriaMetrics::
 curl -Is http://<vmselect>:8481/select/internal/resetRollupResultCache
 ```
 
+vmselect will propagate this call to the rest of the vmselects listed in its `-selectNode` cmd-line flag. If this
+flag isn't set, then cache need to be purged from each vmselect individually.
+
+
 </div>
 
 ### TCP and UDP
diff --git a/docs/vmagent.md b/docs/vmagent.md
index 84b00c109..02f947675 100644
--- a/docs/vmagent.md
+++ b/docs/vmagent.md
@@ -639,6 +639,26 @@ The following articles contain useful information about Prometheus relabeling:
       source_labels: ["instance", "host"]
     ```
 
+  * `keep_if_contains`: keeps the entry if `target_label` contains all the label values listed in `source_labels`,
+    while dropping all the other entries. For example, the following relabeling config keeps targets
+    if `__meta_consul_tags` contains value from the `required_consul_tag` label:
+
+    ```yaml
+    - action: keep_if_contains
+      target_label: __meta_consul_tags
+      source_labels: [required_consul_tag]
+    ```
+
+  * `drop_if_contains`: drops the entry if `target_label` contains all the label values listed in `source_labels`,
+    while keeping all the other entries. For example, the following relabeling config drops targets
+    if `__meta_consul_tag` contains value from the `denied_consul_tag` label:
+
+    ```yaml
+    - action: drop_if_contains
+      target_label: __meta_consul_tags
+      source_labels: [denied_consul_tag]
+    ```
+
   * `keep_metrics`: keeps all the metrics with names matching the given `regex`,
     while dropping all the other metrics. For example, the following relabeling config keeps metrics
     with `foo` and `bar` names, while dropping all the other metrics:
@@ -1205,6 +1225,25 @@ which can be downloaded for evaluation from [releases](https://github.com/Victor
     	The following optional suffixes are supported: s (second), m (minute), h (hour), d (day), w (week), y (year). If suffix isn't set, then the duration is counted in months (default 60s)
 ```
 
+## How much disk space should you provide for the vmagent persistence queue?
+
+vmagent buffers scraped or received data at the `-remoteWrite.tmpDataPath` file system directory (aka persistent queue) until it is sent to `-remoteWrite.url`. The directory can grow large when remote storage is unavailable for extended periods of time and if the maximum directory size isn't limited with `-remoteWrite.maxDiskUsagePerURL` command-line flag. The buffered metrics are sent to remote storage as soon as the connection to the remote storage is repaired.
+
+To estimate the disk size allocated or `-remoteWrite.maxDiskUsagePerURL` for a persistent queue take into account the following attributes:
+1. the size in bytes of data stream sent by vmagent:
+Run query `sum(rate(vmagent_remotewrite_bytes_sent_total[1h])) by(instance)` in [vmui](https://docs.victoriametrics.com/#vmui) or Grafana to get the amount of **bytes** sent by each vmagent instance (in k8s you might want to `sum by(pod)` instead) per second.
+2. the amount of **time** a persistent queue should keep the data before starting to drop it.
+If the persistent queue should be able to retain the data for at least 6h, then multiply the amount of bytes sent by vmagent per-second by 6*3600s to get the approximate queue size on disk.
+
+For example, expression [sum(rate(vmagent_remotewrite_bytes_sent_total[1h])) by(instance) * 6 * 3600 / 1Gi](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/#/?g0.expr=sum%28rate%28vmagent_remotewrite_bytes_sent_total%5B1h%5D%29%29+by%28instance%29+*+6+*+3600+%2F+1Gi&g0.range_input=30m&g0.end_input=2023-11-30T14%3A55%3A14&g0.tab=1&g0.relative_time=last_30_minutes) suggests that there are two vmagents: one needs 2Gi and second 21Gi of disk space available for persistent queue to retain data for 6h.
+
+Additional notes:
+1. For `vmagent_.*` metrics to be available for querying ensure that [monitoring](#monitoring) is configured.
+1. Re-evaluate the estimation each time when:
+    * there is an increase in the amount of metrics to process
+    * there is a change in [relabeling rules](https://docs.victoriametrics.com/vmagent.html#relabeling) which could increase the amount of sent metrics
+    * you add a new  `-remoteWrite.url`
+1. The minimum disk size to allocate for the persistent queue is 500Mi per each `-remoteWrite.url` .
 ## Kafka integration
 
 [Enterprise version](https://docs.victoriametrics.com/enterprise.html) of `vmagent` can read and write metrics from / to Kafka:
@@ -1414,7 +1453,7 @@ See the docs at https://docs.victoriametrics.com/vmagent.html .
   -csvTrimTimestamp duration
      Trim timestamps when importing csv data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
   -datadog.maxInsertRequestSize size
-     The maximum size in bytes of a single DataDog POST request to /api/v1/series
+     The maximum size in bytes of a single DataDog POST request to /api/v1/series, /api/v2/series, /api/beta/sketches
      Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 67108864)
   -datadog.sanitizeMetricName
      Sanitize metric names for the ingested DataDog data to comply with DataDog behaviour described at https://docs.datadoghq.com/metrics/custom_metrics/#naming-custom-metrics (default true)
diff --git a/docs/vmalert-tool.md b/docs/vmalert-tool.md
index 83b681cc3..ef832cdfc 100644
--- a/docs/vmalert-tool.md
+++ b/docs/vmalert-tool.md
@@ -33,6 +33,31 @@ except `promql_expr_test` field. Use `metricsql_expr_test` field name instead. T
 validates and executes [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html) expressions,
 which aren't always backward compatible with [PromQL](https://prometheus.io/docs/prometheus/latest/querying/basics/).
 
+### Limitations
+
+* vmalert-tool evaluates all the groups defined in `rule_files` using `evaluation_interval`(default `1m`) instead of `interval` under each rule group.
+* vmalert-tool shares the same limitation with [vmalert](https://docs.victoriametrics.com/vmalert.html#limitations) on chaining rules under one group:
+
+>by default, rules execution is sequential within one group, but persistence of execution results to remote storage is asynchronous. Hence, user shouldn’t rely on chaining of recording rules when result of previous recording rule is reused in the next one;
+
+For example, you have recording rule A and alerting rule B in the same group, and rule B's expression is based on A's results.
+Rule B won't get the latest data of A, since data didn't persist to remote storage yet. 
+The workaround is to divide them in two groups and put groupA in front of groupB (or use `group_eval_order` to define the evaluation order).
+In this way, vmalert-tool makes sure that the results of groupA must be written to storage before evaluating groupB:
+
+```yaml
+groups:
+- name: groupA
+  rules:
+  - record: A
+    expr: sum(xxx)
+- name: groupB
+  rules:
+  - alert: B
+    expr: A >= 0.75
+    for: 1m 
+```
+
 ### Test file format
 
 The configuration format for files specified in `--files` cmd-line flag is the following:
diff --git a/docs/vmauth.md b/docs/vmauth.md
index 867a57cd9..dd80fb3dc 100644
--- a/docs/vmauth.md
+++ b/docs/vmauth.md
@@ -12,20 +12,23 @@ aliases:
 # vmauth
 
 `vmauth` is a simple auth proxy, router and [load balancer](#load-balancing) for [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics).
-It reads auth credentials from `Authorization` http header ([Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication), `Bearer token` and [InfluxDB authorization](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1897) is supported),
+It reads auth credentials from `Authorization` http header ([Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication),
+`Bearer token` and [InfluxDB authorization](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1897) is supported),
 matches them against configs pointed by [-auth.config](#auth-config) command-line flag and proxies incoming HTTP requests to the configured per-user `url_prefix` on successful match.
 The `-auth.config` can point to either local file or to http url.
 
 ## Quick start
 
 Just download `vmutils-*` archive from [releases page](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest), unpack it
-and pass the following flag to `vmauth` binary in order to start authorizing and routing requests:
+and pass the following flag to `vmauth` binary in order to start authorizing and proxying requests:
 
 ```console
 /path/to/vmauth -auth.config=/path/to/auth/config.yml
 ```
 
-After that `vmauth` starts accepting HTTP requests on port `8427` and routing them according to the provided [-auth.config](#auth-config).
+The `-auth.config` command-line flag must point to valid [config](#auth-config). See [use cases](#use-cases) with typical `-auth.config` examples.
+
+`vmauth` accepts HTTP requests on port `8427` and proxies them according to the provided [-auth.config](#auth-config).
 The port can be modified via `-httpListenAddr` command-line flag.
 
 The auth config can be reloaded via the following ways:
@@ -43,10 +46,203 @@ Pass `-help` to `vmauth` in order to see all the supported command-line flags wi
 Feel free [contacting us](mailto:info@victoriametrics.com) if you need customized auth proxy for VictoriaMetrics with the support of LDAP, SSO, RBAC, SAML,
 accounting and rate limiting such as [vmgateway](https://docs.victoriametrics.com/vmgateway.html).
 
+## Use cases
+
+* [Simple HTTP proxy](#simple-http-proxy)
+* [Generic HTTP proxy for different backends](#generic-http-proxy-for-different-backends)
+* [Generic HTTP load balancer](#generic-http-load-balancer)
+* [Load balancer for vmagent](#load-balancer-for-vmagent)
+* [Load balancer for VictoriaMetrics cluster](#load-balancer-for-victoriametrics-cluster)
+* [TLS termination proxy](#tls-termination-proxy)
+* [Basic Auth proxy](#basic-auth-proxy)
+* [Bearer Token auth proxy](#bearer-token-auth-proxy)
+* [Per-tenant authorization](#per-tenant-authorization)
+* [Enforcing query args](#enforcing-query-args)
+
+### Simple HTTP proxy
+
+The following [`-auth.config`](#auth-config) instructs `vmauth` to proxy all the incoming requests to the given backend.
+For example, requests to `http://vmauth:8427/foo/bar` are proxied to `http://backend/foo/bar`:
+
+```yml
+unauthorized_user:
+  url_prefix: "http://backend/"
+```
+
+### Generic HTTP proxy for different backends
+
+`vmauth` can proxy requests to different backends depending on the requested path.
+For example, the following [`-auth.config`](#auth-config) instructs `vmauth` to make the following:
+
+- Requests starting with `/app1/` are proxied to `http://app1-backend/`. For example, the request to `http://vmauth:8427/app1/foo/bar?baz=qwe`
+  is proxied to `http://app1-backend/foo/bar?baz=qwe`.
+- Requests starting with `/app2/` are proxied to `http://app2-backend/`. For example, the request to `http://vmauth:8427/app2/index.html`
+  is proxied to `http://app2-backend/index.html`.
+- Other requests are proxied to `http://some-backend/404-page.html`, while the requested path is passed via `request_path` query arg.
+  For example, the request to `http://vmauth:8427/foo/bar?baz=qwe` is proxied to `http://some-backend/404-page.html?request_path=%2Ffoo%2Fbar%3Fbaz%3Dqwe`.
+
+```yml
+unauthorized_user:
+  url_map:
+  - src_paths:
+    - "/app1/.*"
+    drop_src_path_prefix_parts: 1
+    url_prefix: "http://app1-backend/"
+  - src_paths:
+    - "/app2/.*"
+    drop_src_path_prefix_parts: 1
+    url_prefix: "http://app2-backend/"
+  default_url: http://some-backend/404-page.html
+```
+
+### Generic HTTP load balancer
+
+`vmauth` can balance load among multiple HTTP backends in least-loaded round-robin mode.
+For example, the following [`-auth.config`](#auth-config) instructs `vmauth` to spread load load among multiple application instances:
+
+```yml
+unauthorized_user:
+  url_prefix:
+  - "http://app-instance-1/"
+  - "http://app-instance-2/"
+  - "http://app-instance-3/"
+```
+
+### Load balancer for vmagent
+
+If [vmagent](https://docs.victoriametrics.com/vmagent.html) is used for processing [data push requests](https://docs.victoriametrics.com/vmagent.html#how-to-push-data-to-vmagent),
+then it is possible to scale the performance of data processing at `vmagent` by spreading load among multiple identically configured `vmagent` instances.
+This can be done with the following [config](#auth-config) for `vmagent`:
+
+```yml
+unauthorized_user:
+  url_map:
+  - src_paths:
+    - "/prometheus/api/v1/write"
+    - "/influx/write"
+    - "/api/v1/import"
+    - "/api/v1/import/.+"
+    url_prefix:
+    - "http://vmagent-1:8429/"
+    - "http://vmagent-2:8429/"
+    - "http://vmagent-3:8429/"
+```
+
+### Load balancer for VictoriaMetrics cluster
+
+[VictoriaMetrics cluster](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html) accepts incoming data via `vminsert` nodes
+and processes incoming requests via `vmselect` nodes according to [these docs](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html#architecture-overview).
+`vmauth` can be used for balancing both `insert` and `select` requests among `vminsert` and `vmselect` nodes, when the following [`-auth.config`](#auth-config) is used:
+
+```yml
+unauthorized_user:
+  url_map:
+  - src_paths:
+    - "/insert/.+"
+    url_prefix:
+    - "http://vminsert-1:8480/"
+    - "http://vminsert-2:8480/"
+    - "http://vminsert-3:8480/"
+  - src_paths:
+    - "/select/.+"
+    url_prefix:
+    - "http://vmselect-1:8481/"
+    - "http://vmselect-2:8481/"
+```
+
+### TLS termination proxy
+
+`vmauth` can terminate HTTPS requests to backend services when it runs with the following command-line flags:
+
+```
+/path/to/vmauth -tls -tlsKeyFile=/path/to/tls_key_file -tlsCertFile=/path/to/tls_cert_file -httpListenAddr=0.0.0.0:443
+```
+
+* `-httpListenAddr` sets the address to listen for incoming HTTPS requests
+* `-tls` enables accepting TLS connections at `-httpListenAddr`
+* `-tlsKeyFile` sets the path to TLS certificate key file
+* `-tlsCertFile` sets the path to TLS certificate file
+
+### Basic Auth proxy
+
+`vmauth` can authorize access to backends depending on the provided [Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication) request headers.
+For example, the following [config](#auth-config) proxies requests to [single-node VictoriaMetrics](https://docs.victoriametrics.com/)
+if they contain Basic Auth header with the given `username` and `password`:
+
+```yml
+users:
+- username: foo
+  password: bar
+  url_prefix: "http://victoria-metrics:8428/"
+```
+
+See also [security docs](#security).
+
+### Bearer Token auth proxy
+
+`vmauth` can authorize access to backends depending on the provided `Bearer Token` request headers.
+For example, the following [config](#auth-config) proxies requests to [single-node VictoriaMetrics](https://docs.victoriametrics.com/)
+if they contain the given `bearer_token`:
+
+```yml
+users:
+- bearer_token: ABCDEF
+  url_prefix: "http://victoria-metrics:8428/"
+```
+
+See also [security docs](#security).
+
+### Per-tenant authorization
+
+The following [`-auth.config`](#auth-config) instructs proxying `insert` and `select` requests from the [Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication)
+user `tenant1` to the [tenant](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html#multitenancy) `1`,
+while requests from the user `tenant2` are sent to tenant `2`:
+
+```yml
+users:
+- username: tenant1
+  password: "***"
+  url_map:
+  - src_paths:
+    - "/api/v1/write"
+    url_prefix: "http://vminsert-backend:8480/insert/1/prometheus/"
+  - src_paths:
+    - "/api/v1/query"
+    - "/api/v1/query_range"
+    - "/api/v1/series"
+    - "/api/v1/labels"
+    - "/api/v1/label/.+/values"
+    url_prefix: "http://vmselect-backend:8481/select/1/prometheus/"
+- username: tenant2
+  password: "***"
+  url_map:
+  - src_paths:
+    - "/api/v1/write"
+    url_prefix: "http://vminsert-backend:8480/insert/2/prometheus/"
+  - src_paths:
+    - "/api/v1/query"
+    - "/api/v1/query_range"
+    - "/api/v1/series"
+    - "/api/v1/labels"
+    - "/api/v1/label/.+/values"
+    url_prefix: "http://vmselect-backend:8481/select/2/prometheus/"
+```
+
+### Enforcing query args
+
+`vmauth` can be configured for adding some mandatory query args before proxying requests to backends.
+For example, the following [config](#auth-config) adds [`extra_label`](https://docs.victoriametrics.com/#prometheus-querying-api-enhancements)
+to all the requests, which are proxied to [single-node VictoriaMetrics](https://docs.victoriametrics.com/):
+
+```yml
+unauthorized_user:
+  url_prefix: "http://victoria-metrics:8428/?extra_label=foo=bar"
+```
+
 ## Dropping request path prefix
 
 By default `vmauth` doesn't drop the path prefix from the original request when proxying the request to the matching backend.
-Sometimes it is needed to drop path prefix before routing the request to the backend. This can be done by specifying the number of `/`-delimited
+Sometimes it is needed to drop path prefix before proxying the request to the backend. This can be done by specifying the number of `/`-delimited
 prefix parts to drop from the request path via `drop_src_path_prefix_parts` option at `url_map` level or at `user` level.
 
 For example, if you need to serve requests to [vmalert](https://docs.victoriametrics.com/vmalert.html) at `/vmalert/` path prefix,
@@ -244,14 +440,14 @@ users:
 
   # All the requests to http://vmauth:8427 with the given Basic Auth (username:password)
   # are proxied to http://localhost:8428 with extra_label=team=dev query arg.
-  # For example, http://vmauth:8427/api/v1/query is routed to http://localhost:8428/api/v1/query?extra_label=team=dev
+  # For example, http://vmauth:8427/api/v1/query is proxied to http://localhost:8428/api/v1/query?extra_label=team=dev
 - username: "local-single-node2"
   password: "***"
   url_prefix: "http://localhost:8428?extra_label=team=dev"
 
   # All the requests to http://vmauth:8427 with the given Basic Auth (username:password)
   # are proxied to https://localhost:8428.
-  # For example, http://vmauth:8427/api/v1/query is routed to https://localhost/api/v1/query
+  # For example, http://vmauth:8427/api/v1/query is proxied to https://localhost/api/v1/query
   # TLS verification is skipped for https://localhost.
 - username: "local-single-node-with-tls"
   password: "***"
@@ -323,9 +519,9 @@ users:
   - "http://default1:8888/unsupported_url_handler"
   - "http://default2:8888/unsupported_url_handler"
 
-# Requests without Authorization header are routed according to `unauthorized_user` section.
-# Requests are routed in round-robin fashion between `url_prefix` backends.
-# The deny_partial_response query arg is added to all the routed requests.
+# Requests without Authorization header are proxied according to `unauthorized_user` section.
+# Requests are proxied in round-robin fashion between `url_prefix` backends.
+# The deny_partial_response query arg is added to all the proxied requests.
 # The requests are re-tried if url_prefix backends send 500 or 503 response status codes.
 # Note that the unauthorized_user section takes precedence when processing a route without credentials,
 # even if such a route also exists in the users section (see https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5236).
diff --git a/go.mod b/go.mod
index d2e7837d4..c180d2f97 100644
--- a/go.mod
+++ b/go.mod
@@ -11,8 +11,8 @@ require (
 	// Do not use the original github.com/valyala/fasthttp because of issues
 	// like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b
 	github.com/VictoriaMetrics/fasthttp v1.2.0
-	github.com/VictoriaMetrics/metrics v1.24.0
-	github.com/VictoriaMetrics/metricsql v0.69.0
+	github.com/VictoriaMetrics/metrics v1.25.3
+	github.com/VictoriaMetrics/metricsql v0.70.0
 	github.com/aws/aws-sdk-go-v2 v1.23.0
 	github.com/aws/aws-sdk-go-v2/config v1.25.1
 	github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.8
@@ -35,7 +35,7 @@ require (
 	github.com/valyala/quicktemplate v1.7.0
 	golang.org/x/net v0.18.0
 	golang.org/x/oauth2 v0.14.0
-	golang.org/x/sys v0.14.0
+	golang.org/x/sys v0.15.0
 	google.golang.org/api v0.150.0
 	gopkg.in/yaml.v2 v2.4.0
 )
diff --git a/go.sum b/go.sum
index 7617c98c4..9368e15df 100644
--- a/go.sum
+++ b/go.sum
@@ -62,10 +62,11 @@ github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjC
 github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
 github.com/VictoriaMetrics/fasthttp v1.2.0 h1:nd9Wng4DlNtaI27WlYh5mGXCJOmee/2c2blTJwfyU9I=
 github.com/VictoriaMetrics/fasthttp v1.2.0/go.mod h1:zv5YSmasAoSyv8sBVexfArzFDIGGTN4TfCKAtAw7IfE=
-github.com/VictoriaMetrics/metrics v1.24.0 h1:ILavebReOjYctAGY5QU2F9X0MYvkcrG3aEn2RKa1Zkw=
 github.com/VictoriaMetrics/metrics v1.24.0/go.mod h1:eFT25kvsTidQFHb6U0oa0rTrDRdz4xTYjpL8+UPohys=
-github.com/VictoriaMetrics/metricsql v0.69.0 h1:6np68zGOnMiGEJR/rCvywS1gbLGXVrmQC3BKydsbWHw=
-github.com/VictoriaMetrics/metricsql v0.69.0/go.mod h1:k4UaP/+CjuZslIjd+kCigNG9TQmUqh5v0TP/nMEy90I=
+github.com/VictoriaMetrics/metrics v1.25.3 h1:Zcxyj8JbAB6CQU51Er3D7RBRupcP55DevVQi9cFqo2Q=
+github.com/VictoriaMetrics/metrics v1.25.3/go.mod h1:ZKmlI+QN6b9LUC0OiHNp2LiGQGlBy4U1re6Slooln1o=
+github.com/VictoriaMetrics/metricsql v0.70.0 h1:G0k/m1yAF6pmk0dM3VT9/XI5PZ8dL7EbcLhREf4bgeI=
+github.com/VictoriaMetrics/metricsql v0.70.0/go.mod h1:k4UaP/+CjuZslIjd+kCigNG9TQmUqh5v0TP/nMEy90I=
 github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
 github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -617,8 +618,9 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
 golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
diff --git a/lib/promrelabel/config.go b/lib/promrelabel/config.go
index 137ff4b58..fdd2f853c 100644
--- a/lib/promrelabel/config.go
+++ b/lib/promrelabel/config.go
@@ -291,6 +291,26 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
 		if targetLabel == "" {
 			return nil, fmt.Errorf("missing `target_label` for `action=replace_all`")
 		}
+	case "keep_if_contains":
+		if targetLabel == "" {
+			return nil, fmt.Errorf("`target_label` must be set for `action=keep_if_containes`")
+		}
+		if len(sourceLabels) == 0 {
+			return nil, fmt.Errorf("`source_labels` must contain at least a single entry for `action=keep_if_contains`")
+		}
+		if rc.Regex != nil {
+			return nil, fmt.Errorf("`regex` cannot be used for `action=keep_if_contains`")
+		}
+	case "drop_if_contains":
+		if targetLabel == "" {
+			return nil, fmt.Errorf("`target_label` must be set for `action=drop_if_containes`")
+		}
+		if len(sourceLabels) == 0 {
+			return nil, fmt.Errorf("`source_labels` must contain at least a single entry for `action=drop_if_contains`")
+		}
+		if rc.Regex != nil {
+			return nil, fmt.Errorf("`regex` cannot be used for `action=drop_if_contains`")
+		}
 	case "keep_if_equal":
 		if len(sourceLabels) < 2 {
 			return nil, fmt.Errorf("`source_labels` must contain at least two entries for `action=keep_if_equal`; got %q", sourceLabels)
diff --git a/lib/promrelabel/relabel.go b/lib/promrelabel/relabel.go
index e2d5d68da..be3c5f6fa 100644
--- a/lib/promrelabel/relabel.go
+++ b/lib/promrelabel/relabel.go
@@ -70,11 +70,15 @@ func (prc *parsedRelabelConfig) String() string {
 //
 // It returns DebugStep list - one entry per each applied relabeling step.
 func (pcs *ParsedConfigs) ApplyDebug(labels []prompbmarshal.Label) ([]prompbmarshal.Label, []DebugStep) {
-	labels, dss := pcs.applyInternal(labels, 0, true)
+	// Protect from overwriting labels between len(labels) and cap(labels) by limiting labels capacity to its length.
+	labels, dss := pcs.applyInternal(labels[:len(labels):len(labels)], 0, true)
 	return labels, dss
 }
 
 // Apply applies pcs to labels starting from the labelsOffset.
+//
+// Apply() may add additional labels after the len(labels), so make sure it doesn't corrupt in-use labels
+// stored between len(labels) and cap(labels).
 func (pcs *ParsedConfigs) Apply(labels []prompbmarshal.Label, labelsOffset int) []prompbmarshal.Label {
 	labels, _ = pcs.applyInternal(labels, labelsOffset, false)
 	return labels
@@ -252,6 +256,32 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
 			labels = setLabelValue(labels, labelsOffset, prc.TargetLabel, valueStr)
 		}
 		return labels
+	case "keep_if_contains":
+		// Keep the entry if target_label contains all the label values listed in source_labels.
+		// For example, the following relabeling rule would leave the entry if __meta_consul_tags
+		// contains values of __meta_required_tag1 and __meta_required_tag2:
+		//
+		//   - action: keep_if_contains
+		//     target_label: __meta_consul_tags
+		//     source_labels: [__meta_required_tag1, __meta_required_tag2]
+		//
+		if containsAllLabelValues(src, prc.TargetLabel, prc.SourceLabels) {
+			return labels
+		}
+		return labels[:labelsOffset]
+	case "drop_if_contains":
+		// Drop the entry if target_label contains all the label values listed in source_labels.
+		// For example, the following relabeling rule would drop the entry if __meta_consul_tags
+		// contains values of __meta_required_tag1 and __meta_required_tag2:
+		//
+		//   - action: drop_if_contains
+		//     target_label: __meta_consul_tags
+		//     source_labels: [__meta_required_tag1, __meta_required_tag2]
+		//
+		if containsAllLabelValues(src, prc.TargetLabel, prc.SourceLabels) {
+			return labels[:labelsOffset]
+		}
+		return labels
 	case "keep_if_equal":
 		// Keep the entry if all the label values in source_labels are equal.
 		// For example:
@@ -485,6 +515,17 @@ func (prc *parsedRelabelConfig) expandCaptureGroups(template, source string, mat
 
 var relabelBufPool bytesutil.ByteBufferPool
 
+func containsAllLabelValues(labels []prompbmarshal.Label, targetLabel string, sourceLabels []string) bool {
+	targetLabelValue := getLabelValue(labels, targetLabel)
+	for _, sourceLabel := range sourceLabels {
+		v := getLabelValue(labels, sourceLabel)
+		if !strings.Contains(targetLabelValue, v) {
+			return false
+		}
+	}
+	return true
+}
+
 func areEqualLabelValues(labels []prompbmarshal.Label, labelNames []string) bool {
 	if len(labelNames) < 2 {
 		logger.Panicf("BUG: expecting at least 2 labelNames; got %d", len(labelNames))
diff --git a/lib/protoparser/datadog/api/series/v1/api.go b/lib/protoparser/datadog/api/series/v1/api.go
new file mode 100644
index 000000000..cecf98f0a
--- /dev/null
+++ b/lib/protoparser/datadog/api/series/v1/api.go
@@ -0,0 +1,102 @@
+package datadog
+
+import (
+	"encoding/json"
+
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog"
+)
+
+// Request represents /api/v1/series request
+type Request struct {
+	Series []series `json:"series"`
+}
+
+// Unmarshal decodes byte array to series v1 Request struct
+func (r *Request) Unmarshal(b []byte) error {
+	return json.Unmarshal(b, r)
+}
+
+// Extract iterates fn execution over all timeseries from series v1 Request
+func (r *Request) Extract(fn func(prompbmarshal.TimeSeries) error, sanitizeFn func(string) string) error {
+	currentTimestamp := int64(fasttime.UnixTimestamp())
+	for i := range r.Series {
+		s := r.Series[i]
+		samples := make([]prompbmarshal.Sample, 0, len(s.Points))
+		for j := range s.Points {
+			p := s.Points[j]
+			ts, val := p[0], p[1]
+			if ts <= 0 {
+				ts = float64(currentTimestamp)
+			}
+			samples = append(samples, prompbmarshal.Sample{
+				Timestamp: int64(ts * 1000),
+				Value:     val,
+			})
+		}
+		ts := prompbmarshal.TimeSeries{
+			Samples: samples,
+			Labels:  s.getLabels(sanitizeFn),
+		}
+		if err := fn(ts); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// series represents a series item from DataDog POST request to /api/v1/series
+//
+// See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
+type series struct {
+	Metric string `json:"metric"`
+	Host   string `json:"host"`
+
+	// The device field does not appear in the datadog docs, but datadog-agent does use it.
+	// Datadog agent (v7 at least), removes the tag "device" and adds it as its own field. Why? That I don't know!
+	// https://github.com/DataDog/datadog-agent/blob/0ada7a97fed6727838a6f4d9c87123d2aafde735/pkg/metrics/series.go#L84-L105
+	Device string `json:"device"`
+
+	// Do not decode Interval, since it isn't used by VictoriaMetrics
+	// Interval int64 `json:"interval"`
+
+	Points []point  `json:"points"`
+	Tags   []string `json:"tags"`
+
+	// Do not decode type, since it isn't used by VictoriaMetrics
+	// Type string `json:"type"`
+}
+
+func (s *series) getLabels(sanitizeFn func(string) string) []prompbmarshal.Label {
+	labels := []prompbmarshal.Label{{
+		Name:  "__name__",
+		Value: sanitizeFn(s.Metric),
+	}}
+	if s.Host != "" {
+		labels = append(labels, prompbmarshal.Label{
+			Name:  "host",
+			Value: s.Host,
+		})
+	}
+	if s.Device != "" {
+		labels = append(labels, prompbmarshal.Label{
+			Name:  "device",
+			Value: s.Device,
+		})
+	}
+	for _, tag := range s.Tags {
+		name, value := datadog.SplitTag(tag)
+		if name == "host" {
+			name = "exported_host"
+		}
+		labels = append(labels, prompbmarshal.Label{
+			Name:  sanitizeFn(name),
+			Value: value,
+		})
+	}
+	return labels
+}
+
+// point represents a point from DataDog POST request to /api/v1/series
+type point [2]float64
diff --git a/lib/protoparser/datadog/api/series/v2/api.go b/lib/protoparser/datadog/api/series/v2/api.go
new file mode 100644
index 000000000..de058fd54
--- /dev/null
+++ b/lib/protoparser/datadog/api/series/v2/api.go
@@ -0,0 +1,89 @@
+package datadog
+
+import (
+	"encoding/json"
+
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog"
+)
+
+// Request represents a sketches item from DataDog POST request to /api/v2/series
+type Request struct {
+	Series []series `json:"series"`
+}
+
+// Unmarshal decodes byte array to series v2 Request struct
+func (r *Request) Unmarshal(b []byte) error {
+	return json.Unmarshal(b, r)
+}
+
+// Extract iterates fn execution over all timeseries from series v2 request
+func (r *Request) Extract(fn func(prompbmarshal.TimeSeries) error, sanitizeFn func(string) string) error {
+	for i := range r.Series {
+		s := r.Series[i]
+		samples := make([]prompbmarshal.Sample, 0, len(s.Points))
+		for j := range s.Points {
+			p := s.Points[j]
+			samples = append(samples, prompbmarshal.Sample{
+				Timestamp: p.Timestamp * 1000,
+				Value:     p.Value,
+			})
+		}
+		ts := prompbmarshal.TimeSeries{
+			Samples: samples,
+			Labels:  s.getLabels(sanitizeFn),
+		}
+		if err := fn(ts); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// series represents a series item from DataDog POST request to /api/v2/series
+//
+// See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
+type series struct {
+	Metric    string     `json:"metric"`
+	Resources []resource `json:"resources"`
+	Points    []point    `json:"points"`
+	Tags      []string   `json:"tags"`
+
+	// Do not decode Type, since it isn't used by VictoriaMetrics
+	// Type string `json:"type"`
+}
+
+type resource struct {
+	Name string `json:"name"`
+	Type string `json:"type"`
+}
+
+func (s *series) getLabels(sanitizeFn func(string) string) []prompbmarshal.Label {
+	labels := []prompbmarshal.Label{{
+		Name:  "__name__",
+		Value: sanitizeFn(s.Metric),
+	}}
+	for _, res := range s.Resources {
+		labels = append(labels, prompbmarshal.Label{
+			Name:  sanitizeFn(res.Type),
+			Value: res.Name,
+		})
+	}
+	for _, tag := range s.Tags {
+		name, value := datadog.SplitTag(tag)
+		if name == "host" {
+			name = "exported_host"
+		}
+		labels = append(labels, prompbmarshal.Label{
+			Name:  sanitizeFn(name),
+			Value: value,
+		})
+	}
+	return labels
+}
+
+// point represents a point from DataDog POST request to /api/v2/series
+type point struct {
+	Timestamp int64   `json:"timestamp"`
+	Value     float64 `json:"value"`
+}
diff --git a/lib/protoparser/datadog/api/sketches/beta/README.md b/lib/protoparser/datadog/api/sketches/beta/README.md
new file mode 100644
index 000000000..8ac8b117c
--- /dev/null
+++ b/lib/protoparser/datadog/api/sketches/beta/README.md
@@ -0,0 +1,32 @@
+# Datadog proto files
+
+Content copied from https://github.com/DataDog/agent-payload/blob/master/proto/metrics/agent_payload.proto
+
+## Requirements
+- protoc binary [link](http://google.github.io/proto-lens/installing-protoc.html)
+- golang-proto-gen[link](https://developers.google.com/protocol-buffers/docs/reference/go-generated)
+- custom marshaller [link](https://github.com/planetscale/vtprotobuf)
+
+## Modifications
+
+ Original proto files were modified:
+1) changed package name for `package beta`.
+2) changed import paths - changed directory names.
+3) changed go_package for  `./pb`.
+
+
+## How to generate pbs
+
+ run command:
+ ```bash
+export GOBIN=~/go/bin protoc
+protoc -I=. --go_out=./lib/protoparser/datadog/api/sketches/beta --go-vtproto_out=./lib/protoparser/datadog/api/sketches/beta --plugin protoc-gen-go-vtproto="$GOBIN/protoc-gen-go-vtproto" --go-vtproto_opt=features=unmarshal lib/protoparser/datadog/api/sketches/beta/proto/*.proto
+ ```
+
+ Generated code will be at `lib/protoparser/datadog/api/sketches/beta/pb`
+
+ manually edit it:
+ 
+1) remove all external imports
+2) remove all unneeded methods
+3) replace `unknownFields` with `unknownFields []byte`
diff --git a/lib/protoparser/datadog/api/sketches/beta/api.go b/lib/protoparser/datadog/api/sketches/beta/api.go
new file mode 100644
index 000000000..4c857871d
--- /dev/null
+++ b/lib/protoparser/datadog/api/sketches/beta/api.go
@@ -0,0 +1,95 @@
+package datadog
+
+import (
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog"
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog/api/sketches/beta/pb"
+)
+
+// Request represents a sketches item from DataDog POST request to /api/beta/sketches
+type Request struct {
+	*pb.SketchPayload
+}
+
+// Unmarshal is a wrapper around SketchesPayload Unmarshal method which decodes byte array to SketchPayload struct
+func (r *Request) Unmarshal(b []byte) error {
+	if r.SketchPayload == nil {
+		r.SketchPayload = new(pb.SketchPayload)
+	}
+	return r.SketchPayload.UnmarshalVT(b)
+}
+
+// Extract iterates fn function execution over all timeseries from a sketch payload
+func (r *Request) Extract(fn func(prompbmarshal.TimeSeries) error, sanitizeFn func(string) string) error {
+	var err error
+	for _, sketch := range r.SketchPayload.Sketches {
+		sketchSeries := make([]prompbmarshal.TimeSeries, 5)
+		for _, point := range sketch.Dogsketches {
+			timestamp := point.Ts * 1000
+			updateSeries(sketchSeries, sanitizeFn(sketch.Metric), timestamp, map[string]float64{
+				"max": point.Max,
+				"min": point.Min,
+				"cnt": float64(point.Cnt),
+				"avg": point.Avg,
+				"sum": point.Sum,
+			})
+		}
+		for _, point := range sketch.Distributions {
+			timestamp := point.Ts * 1000
+			updateSeries(sketchSeries, sanitizeFn(sketch.Metric), timestamp, map[string]float64{
+				"max": point.Max,
+				"min": point.Min,
+				"cnt": float64(point.Cnt),
+				"avg": point.Avg,
+				"sum": point.Sum,
+			})
+		}
+		labels := getLabels(sketch, sanitizeFn)
+		for i := range sketchSeries {
+			sketchSeries[i].Labels = append(sketchSeries[i].Labels, labels...)
+			if err = fn(sketchSeries[i]); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func getLabels(sketch *pb.SketchPayload_Sketch, sanitizeFn func(string) string) []prompbmarshal.Label {
+	labels := []prompbmarshal.Label{}
+	if sketch.Host != "" {
+		labels = append(labels, prompbmarshal.Label{
+			Name:  "host",
+			Value: sketch.Host,
+		})
+	}
+	for _, tag := range sketch.Tags {
+		name, value := datadog.SplitTag(tag)
+		if name == "host" {
+			name = "exported_host"
+		}
+		labels = append(labels, prompbmarshal.Label{
+			Name:  sanitizeFn(name),
+			Value: value,
+		})
+	}
+	return labels
+}
+
+func updateSeries(series []prompbmarshal.TimeSeries, metric string, timestamp int64, values map[string]float64) {
+	index := 0
+	for suffix, value := range values {
+		s := series[index]
+		s.Samples = append(s.Samples, prompbmarshal.Sample{
+			Timestamp: timestamp,
+			Value:     value,
+		})
+		if len(s.Labels) == 0 {
+			s.Labels = append(s.Labels, prompbmarshal.Label{
+				Name:  "",
+				Value: metric + "_" + suffix,
+			})
+		}
+		index++
+	}
+}
diff --git a/lib/protoparser/datadog/api/sketches/beta/pb/sketches.pb.go b/lib/protoparser/datadog/api/sketches/beta/pb/sketches.pb.go
new file mode 100644
index 000000000..f567d6a9b
--- /dev/null
+++ b/lib/protoparser/datadog/api/sketches/beta/pb/sketches.pb.go
@@ -0,0 +1,159 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.29.0
+// 	protoc        v3.15.8
+// source: lib/protoparser/datadog/api/sketches/beta/proto/sketches.proto
+
+package pb
+
+type MetricPayload_MetricType int32
+
+const (
+	MetricPayload_UNSPECIFIED MetricPayload_MetricType = 0
+	MetricPayload_COUNT       MetricPayload_MetricType = 1
+	MetricPayload_RATE        MetricPayload_MetricType = 2
+	MetricPayload_GAUGE       MetricPayload_MetricType = 3
+)
+
+// Enum value maps for MetricPayload_MetricType.
+var (
+	MetricPayload_MetricType_name = map[int32]string{
+		0: "UNSPECIFIED",
+		1: "COUNT",
+		2: "RATE",
+		3: "GAUGE",
+	}
+	MetricPayload_MetricType_value = map[string]int32{
+		"UNSPECIFIED": 0,
+		"COUNT":       1,
+		"RATE":        2,
+		"GAUGE":       3,
+	}
+)
+
+func (x MetricPayload_MetricType) Enum() *MetricPayload_MetricType {
+	p := new(MetricPayload_MetricType)
+	*p = x
+	return p
+}
+
+type CommonMetadata struct {
+	unknownFields []byte
+
+	AgentVersion string  `protobuf:"bytes,1,opt,name=agent_version,json=agentVersion,proto3" json:"agent_version,omitempty"`
+	Timezone     string  `protobuf:"bytes,2,opt,name=timezone,proto3" json:"timezone,omitempty"`
+	CurrentEpoch float64 `protobuf:"fixed64,3,opt,name=current_epoch,json=currentEpoch,proto3" json:"current_epoch,omitempty"`
+	InternalIp   string  `protobuf:"bytes,4,opt,name=internal_ip,json=internalIp,proto3" json:"internal_ip,omitempty"`
+	PublicIp     string  `protobuf:"bytes,5,opt,name=public_ip,json=publicIp,proto3" json:"public_ip,omitempty"`
+	ApiKey       string  `protobuf:"bytes,6,opt,name=api_key,json=apiKey,proto3" json:"api_key,omitempty"`
+}
+
+type MetricPayload struct {
+	unknownFields []byte
+
+	Series []*MetricPayload_MetricSeries `protobuf:"bytes,1,rep,name=series,proto3" json:"series,omitempty"`
+}
+
+type EventsPayload struct {
+	unknownFields []byte
+
+	Events   []*EventsPayload_Event `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"`
+	Metadata *CommonMetadata        `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"`
+}
+
+type SketchPayload struct {
+	unknownFields []byte
+
+	Sketches []*SketchPayload_Sketch `protobuf:"bytes,1,rep,name=sketches,proto3" json:"sketches,omitempty"`
+	Metadata *CommonMetadata         `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"`
+}
+
+type MetricPayload_MetricPoint struct {
+	unknownFields []byte
+
+	// metric value
+	Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"`
+	// timestamp for this value in seconds since the UNIX epoch
+	Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+}
+
+type MetricPayload_Resource struct {
+	unknownFields []byte
+
+	Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
+	Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+}
+
+type MetricPayload_MetricSeries struct {
+	unknownFields []byte
+
+	// Resources this series applies to; include at least
+	// { type="host", name=<hostname> }
+	Resources []*MetricPayload_Resource `protobuf:"bytes,1,rep,name=resources,proto3" json:"resources,omitempty"`
+	// metric name
+	Metric string `protobuf:"bytes,2,opt,name=metric,proto3" json:"metric,omitempty"`
+	// tags for this metric
+	Tags []string `protobuf:"bytes,3,rep,name=tags,proto3" json:"tags,omitempty"`
+	// data points for this metric
+	Points []*MetricPayload_MetricPoint `protobuf:"bytes,4,rep,name=points,proto3" json:"points,omitempty"`
+	// type of metric
+	Type MetricPayload_MetricType `protobuf:"varint,5,opt,name=type,proto3,enum=beta.MetricPayload_MetricType" json:"type,omitempty"`
+	// metric unit name
+	Unit string `protobuf:"bytes,6,opt,name=unit,proto3" json:"unit,omitempty"`
+	// source of this metric (check name, etc.)
+	SourceTypeName string `protobuf:"bytes,7,opt,name=source_type_name,json=sourceTypeName,proto3" json:"source_type_name,omitempty"`
+	// interval, in seconds, between samples of this metric
+	Interval int64 `protobuf:"varint,8,opt,name=interval,proto3" json:"interval,omitempty"`
+}
+
+type EventsPayload_Event struct {
+	unknownFields []byte
+
+	Title          string   `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
+	Text           string   `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"`
+	Ts             int64    `protobuf:"varint,3,opt,name=ts,proto3" json:"ts,omitempty"`
+	Priority       string   `protobuf:"bytes,4,opt,name=priority,proto3" json:"priority,omitempty"`
+	Host           string   `protobuf:"bytes,5,opt,name=host,proto3" json:"host,omitempty"`
+	Tags           []string `protobuf:"bytes,6,rep,name=tags,proto3" json:"tags,omitempty"`
+	AlertType      string   `protobuf:"bytes,7,opt,name=alert_type,json=alertType,proto3" json:"alert_type,omitempty"`
+	AggregationKey string   `protobuf:"bytes,8,opt,name=aggregation_key,json=aggregationKey,proto3" json:"aggregation_key,omitempty"`
+	SourceTypeName string   `protobuf:"bytes,9,opt,name=source_type_name,json=sourceTypeName,proto3" json:"source_type_name,omitempty"`
+}
+
+type SketchPayload_Sketch struct {
+	unknownFields []byte
+
+	Metric        string                               `protobuf:"bytes,1,opt,name=metric,proto3" json:"metric,omitempty"`
+	Host          string                               `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"`
+	Distributions []*SketchPayload_Sketch_Distribution `protobuf:"bytes,3,rep,name=distributions,proto3" json:"distributions,omitempty"`
+	Tags          []string                             `protobuf:"bytes,4,rep,name=tags,proto3" json:"tags,omitempty"`
+	Dogsketches   []*SketchPayload_Sketch_Dogsketch    `protobuf:"bytes,7,rep,name=dogsketches,proto3" json:"dogsketches,omitempty"`
+}
+
+type SketchPayload_Sketch_Distribution struct {
+	unknownFields []byte
+
+	Ts    int64     `protobuf:"varint,1,opt,name=ts,proto3" json:"ts,omitempty"`
+	Cnt   int64     `protobuf:"varint,2,opt,name=cnt,proto3" json:"cnt,omitempty"`
+	Min   float64   `protobuf:"fixed64,3,opt,name=min,proto3" json:"min,omitempty"`
+	Max   float64   `protobuf:"fixed64,4,opt,name=max,proto3" json:"max,omitempty"`
+	Avg   float64   `protobuf:"fixed64,5,opt,name=avg,proto3" json:"avg,omitempty"`
+	Sum   float64   `protobuf:"fixed64,6,opt,name=sum,proto3" json:"sum,omitempty"`
+	V     []float64 `protobuf:"fixed64,7,rep,packed,name=v,proto3" json:"v,omitempty"`
+	G     []uint32  `protobuf:"varint,8,rep,packed,name=g,proto3" json:"g,omitempty"`
+	Delta []uint32  `protobuf:"varint,9,rep,packed,name=delta,proto3" json:"delta,omitempty"`
+	Buf   []float64 `protobuf:"fixed64,10,rep,packed,name=buf,proto3" json:"buf,omitempty"`
+}
+
+type SketchPayload_Sketch_Dogsketch struct {
+	unknownFields []byte
+
+	Ts  int64    `protobuf:"varint,1,opt,name=ts,proto3" json:"ts,omitempty"`
+	Cnt int64    `protobuf:"varint,2,opt,name=cnt,proto3" json:"cnt,omitempty"`
+	Min float64  `protobuf:"fixed64,3,opt,name=min,proto3" json:"min,omitempty"`
+	Max float64  `protobuf:"fixed64,4,opt,name=max,proto3" json:"max,omitempty"`
+	Avg float64  `protobuf:"fixed64,5,opt,name=avg,proto3" json:"avg,omitempty"`
+	Sum float64  `protobuf:"fixed64,6,opt,name=sum,proto3" json:"sum,omitempty"`
+	K   []int32  `protobuf:"zigzag32,7,rep,packed,name=k,proto3" json:"k,omitempty"`
+	N   []uint32 `protobuf:"varint,8,rep,packed,name=n,proto3" json:"n,omitempty"`
+}
diff --git a/lib/protoparser/datadog/api/sketches/beta/pb/sketches_vtproto.pb.go b/lib/protoparser/datadog/api/sketches/beta/pb/sketches_vtproto.pb.go
new file mode 100644
index 000000000..e271a177f
--- /dev/null
+++ b/lib/protoparser/datadog/api/sketches/beta/pb/sketches_vtproto.pb.go
@@ -0,0 +1,2349 @@
+// Code generated by protoc-gen-go-vtproto. DO NOT EDIT.
+// protoc-gen-go-vtproto version: v0.5.0
+// source: lib/protoparser/datadog/api/sketches/beta/proto/sketches.proto
+
+package pb
+
+import (
+	binary "encoding/binary"
+	fmt "fmt"
+	io "io"
+	math "math"
+)
+
+func (m *CommonMetadata) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: CommonMetadata: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: CommonMetadata: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field AgentVersion", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.AgentVersion = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Timezone", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Timezone = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 1 {
+				return fmt.Errorf("proto: wrong wireType = %d for field CurrentEpoch", wireType)
+			}
+			var v uint64
+			if (iNdEx + 8) > l {
+				return io.ErrUnexpectedEOF
+			}
+			v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+			iNdEx += 8
+			m.CurrentEpoch = float64(math.Float64frombits(v))
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field InternalIp", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.InternalIp = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 5:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field PublicIp", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.PublicIp = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 6:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ApiKey", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ApiKey = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MetricPayload_MetricPoint) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MetricPayload_MetricPoint: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MetricPayload_MetricPoint: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 1 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
+			}
+			var v uint64
+			if (iNdEx + 8) > l {
+				return io.ErrUnexpectedEOF
+			}
+			v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+			iNdEx += 8
+			m.Value = float64(math.Float64frombits(v))
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
+			}
+			m.Timestamp = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Timestamp |= int64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MetricPayload_Resource) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MetricPayload_Resource: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MetricPayload_Resource: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Type = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MetricPayload_MetricSeries) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MetricPayload_MetricSeries: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MetricPayload_MetricSeries: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Resources", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Resources = append(m.Resources, &MetricPayload_Resource{})
+			if err := m.Resources[len(m.Resources)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Metric", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Metric = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Tags = append(m.Tags, string(dAtA[iNdEx:postIndex]))
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Points", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Points = append(m.Points, &MetricPayload_MetricPoint{})
+			if err := m.Points[len(m.Points)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
+			}
+			m.Type = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Type |= MetricPayload_MetricType(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 6:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Unit", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Unit = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 7:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SourceTypeName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.SourceTypeName = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 8:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Interval", wireType)
+			}
+			m.Interval = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Interval |= int64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MetricPayload) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MetricPayload: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MetricPayload: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Series", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Series = append(m.Series, &MetricPayload_MetricSeries{})
+			if err := m.Series[len(m.Series)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventsPayload_Event) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventsPayload_Event: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventsPayload_Event: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Title = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Text", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Text = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Ts", wireType)
+			}
+			m.Ts = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Ts |= int64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Priority = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 5:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Host", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Host = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 6:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Tags = append(m.Tags, string(dAtA[iNdEx:postIndex]))
+			iNdEx = postIndex
+		case 7:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field AlertType", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.AlertType = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 8:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field AggregationKey", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.AggregationKey = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 9:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SourceTypeName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.SourceTypeName = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventsPayload) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventsPayload: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventsPayload: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Events = append(m.Events, &EventsPayload_Event{})
+			if err := m.Events[len(m.Events)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Metadata == nil {
+				m.Metadata = &CommonMetadata{}
+			}
+			if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *SketchPayload_Sketch_Distribution) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: SketchPayload_Sketch_Distribution: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: SketchPayload_Sketch_Distribution: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Ts", wireType)
+			}
+			m.Ts = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Ts |= int64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Cnt", wireType)
+			}
+			m.Cnt = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Cnt |= int64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 1 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Min", wireType)
+			}
+			var v uint64
+			if (iNdEx + 8) > l {
+				return io.ErrUnexpectedEOF
+			}
+			v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+			iNdEx += 8
+			m.Min = float64(math.Float64frombits(v))
+		case 4:
+			if wireType != 1 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType)
+			}
+			var v uint64
+			if (iNdEx + 8) > l {
+				return io.ErrUnexpectedEOF
+			}
+			v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+			iNdEx += 8
+			m.Max = float64(math.Float64frombits(v))
+		case 5:
+			if wireType != 1 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Avg", wireType)
+			}
+			var v uint64
+			if (iNdEx + 8) > l {
+				return io.ErrUnexpectedEOF
+			}
+			v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+			iNdEx += 8
+			m.Avg = float64(math.Float64frombits(v))
+		case 6:
+			if wireType != 1 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sum", wireType)
+			}
+			var v uint64
+			if (iNdEx + 8) > l {
+				return io.ErrUnexpectedEOF
+			}
+			v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+			iNdEx += 8
+			m.Sum = float64(math.Float64frombits(v))
+		case 7:
+			if wireType == 1 {
+				var v uint64
+				if (iNdEx + 8) > l {
+					return io.ErrUnexpectedEOF
+				}
+				v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+				iNdEx += 8
+				v2 := float64(math.Float64frombits(v))
+				m.V = append(m.V, v2)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflow
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLength
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLength
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				elementCount = packedLen / 8
+				if elementCount != 0 && len(m.V) == 0 {
+					m.V = make([]float64, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint64
+					if (iNdEx + 8) > l {
+						return io.ErrUnexpectedEOF
+					}
+					v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+					iNdEx += 8
+					v2 := float64(math.Float64frombits(v))
+					m.V = append(m.V, v2)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field V", wireType)
+			}
+		case 8:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflow
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.G = append(m.G, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflow
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLength
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLength
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.G) == 0 {
+					m.G = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflow
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.G = append(m.G, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field G", wireType)
+			}
+		case 9:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflow
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.Delta = append(m.Delta, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflow
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLength
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLength
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.Delta) == 0 {
+					m.Delta = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflow
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.Delta = append(m.Delta, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field Delta", wireType)
+			}
+		case 10:
+			if wireType == 1 {
+				var v uint64
+				if (iNdEx + 8) > l {
+					return io.ErrUnexpectedEOF
+				}
+				v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+				iNdEx += 8
+				v2 := float64(math.Float64frombits(v))
+				m.Buf = append(m.Buf, v2)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflow
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLength
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLength
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				elementCount = packedLen / 8
+				if elementCount != 0 && len(m.Buf) == 0 {
+					m.Buf = make([]float64, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint64
+					if (iNdEx + 8) > l {
+						return io.ErrUnexpectedEOF
+					}
+					v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+					iNdEx += 8
+					v2 := float64(math.Float64frombits(v))
+					m.Buf = append(m.Buf, v2)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field Buf", wireType)
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *SketchPayload_Sketch_Dogsketch) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: SketchPayload_Sketch_Dogsketch: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: SketchPayload_Sketch_Dogsketch: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Ts", wireType)
+			}
+			m.Ts = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Ts |= int64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Cnt", wireType)
+			}
+			m.Cnt = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Cnt |= int64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 1 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Min", wireType)
+			}
+			var v uint64
+			if (iNdEx + 8) > l {
+				return io.ErrUnexpectedEOF
+			}
+			v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+			iNdEx += 8
+			m.Min = float64(math.Float64frombits(v))
+		case 4:
+			if wireType != 1 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType)
+			}
+			var v uint64
+			if (iNdEx + 8) > l {
+				return io.ErrUnexpectedEOF
+			}
+			v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+			iNdEx += 8
+			m.Max = float64(math.Float64frombits(v))
+		case 5:
+			if wireType != 1 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Avg", wireType)
+			}
+			var v uint64
+			if (iNdEx + 8) > l {
+				return io.ErrUnexpectedEOF
+			}
+			v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+			iNdEx += 8
+			m.Avg = float64(math.Float64frombits(v))
+		case 6:
+			if wireType != 1 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sum", wireType)
+			}
+			var v uint64
+			if (iNdEx + 8) > l {
+				return io.ErrUnexpectedEOF
+			}
+			v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
+			iNdEx += 8
+			m.Sum = float64(math.Float64frombits(v))
+		case 7:
+			if wireType == 0 {
+				var v int32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflow
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= int32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				v = int32((uint32(v) >> 1) ^ uint32(((v&1)<<31)>>31))
+				m.K = append(m.K, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflow
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLength
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLength
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.K) == 0 {
+					m.K = make([]int32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v int32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflow
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= int32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					v = int32((uint32(v) >> 1) ^ uint32(((v&1)<<31)>>31))
+					m.K = append(m.K, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field K", wireType)
+			}
+		case 8:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflow
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.N = append(m.N, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflow
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLength
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLength
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.N) == 0 {
+					m.N = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflow
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.N = append(m.N, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field N", wireType)
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *SketchPayload_Sketch) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: SketchPayload_Sketch: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: SketchPayload_Sketch: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Metric", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Metric = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Host", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Host = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Distributions", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Distributions = append(m.Distributions, &SketchPayload_Sketch_Distribution{})
+			if err := m.Distributions[len(m.Distributions)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Tags = append(m.Tags, string(dAtA[iNdEx:postIndex]))
+			iNdEx = postIndex
+		case 7:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Dogsketches", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Dogsketches = append(m.Dogsketches, &SketchPayload_Sketch_Dogsketch{})
+			if err := m.Dogsketches[len(m.Dogsketches)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *SketchPayload) UnmarshalVT(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: SketchPayload: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: SketchPayload: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sketches", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Sketches = append(m.Sketches, &SketchPayload_Sketch{})
+			if err := m.Sketches[len(m.Sketches)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLength
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLength
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Metadata == nil {
+				m.Metadata = &CommonMetadata{}
+			}
+			if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skip(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLength
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+
+func skip(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflow
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflow
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLength
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroup
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLength
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLength        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflow          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroup = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/lib/protoparser/datadog/api/sketches/beta/proto/sketches.proto b/lib/protoparser/datadog/api/sketches/beta/proto/sketches.proto
new file mode 100644
index 000000000..b5afd37b2
--- /dev/null
+++ b/lib/protoparser/datadog/api/sketches/beta/proto/sketches.proto
@@ -0,0 +1,112 @@
+syntax = "proto3";
+
+package beta;
+
+option go_package = "./pb";
+
+import "github.com/gogo/protobuf/gogoproto/gogo.proto";
+
+message CommonMetadata {
+	string agent_version = 1;
+	string timezone = 2;
+	double current_epoch = 3;
+	string internal_ip = 4;
+	string public_ip = 5;
+	string api_key = 6;
+}
+
+message MetricPayload {
+    enum MetricType {
+        UNSPECIFIED = 0;
+        COUNT = 1;
+        RATE = 2;
+        GAUGE = 3;
+    }
+
+    message MetricPoint {
+        // metric value
+        double value = 1;
+        // timestamp for this value in seconds since the UNIX epoch
+        int64 timestamp = 2;
+    }
+
+    message Resource {
+        string type = 1;
+        string name = 2;
+    }
+
+    message MetricSeries {
+        // Resources this series applies to; include at least
+        // { type="host", name=<hostname> }
+        repeated Resource resources = 1;
+        // metric name
+        string metric = 2;
+        // tags for this metric
+        repeated string tags = 3;
+        // data points for this metric
+        repeated MetricPoint points = 4;
+        // type of metric
+        MetricType type = 5;
+        // metric unit name
+        string unit = 6;
+        // source of this metric (check name, etc.)
+        string source_type_name = 7;
+        // interval, in seconds, between samples of this metric
+        int64 interval = 8;
+
+        reserved 9;
+    }
+    repeated MetricSeries series = 1;
+}
+
+message EventsPayload {
+	message Event {
+		string title = 1;
+		string text = 2;
+		int64 ts = 3;
+		string priority = 4;
+		string host = 5;
+		repeated string tags = 6;
+		string alert_type = 7;
+		string aggregation_key = 8;
+		string source_type_name = 9;
+	}
+	repeated Event events = 1;
+	CommonMetadata metadata = 2;
+}
+
+message SketchPayload {
+	message Sketch {
+		message Distribution {
+			int64 ts = 1;
+			int64 cnt = 2;
+			double min = 3;
+			double max = 4;
+			double avg = 5;
+			double sum = 6;
+			repeated double v = 7;
+			repeated uint32 g = 8;
+			repeated uint32 delta = 9;
+			repeated double buf = 10;
+		}
+		message Dogsketch {
+			int64 ts = 1;
+			int64 cnt = 2;
+			double min = 3;
+			double max = 4;
+			double avg = 5;
+			double sum = 6;
+			repeated sint32 k = 7;
+			repeated uint32 n = 8;
+		}
+		string metric = 1;
+		string host = 2;
+		repeated Distribution distributions = 3 [(gogoproto.nullable) = false];
+		repeated string tags = 4;
+                reserved 5, 6;
+				reserved "distributionsK", "distributionsC";
+		repeated Dogsketch dogsketches = 7 [(gogoproto.nullable) = false];
+	}
+	repeated Sketch sketches = 1 [(gogoproto.nullable) = false];
+	CommonMetadata metadata = 2 [(gogoproto.nullable) = false];
+}
diff --git a/lib/protoparser/datadog/parser.go b/lib/protoparser/datadog/parser.go
index c32a9c99d..970adc0d1 100644
--- a/lib/protoparser/datadog/parser.go
+++ b/lib/protoparser/datadog/parser.go
@@ -1,11 +1,9 @@
 package datadog
 
 import (
-	"encoding/json"
-	"fmt"
 	"strings"
 
-	"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
 )
 
 // SplitTag splits DataDog tag into tag name and value.
@@ -20,98 +18,10 @@ func SplitTag(tag string) (string, string) {
 	return tag[:n], tag[n+1:]
 }
 
-// Request represents DataDog POST request to /api/v1/series
+// Request represents DataDog submit metrics request
 //
 // See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
-type Request struct {
-	Series []Series `json:"series"`
-}
-
-func (req *Request) reset() {
-	// recursively reset all the fields in req in order to avoid field value
-	// re-use in json.Unmarshal() when the corresponding field is missing
-	// in the unmarshaled JSON.
-	// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3432
-	series := req.Series
-	for i := range series {
-		series[i].reset()
-	}
-	req.Series = series[:0]
-}
-
-// Unmarshal unmarshals DataDog /api/v1/series request body from b to req.
-//
-// See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
-//
-// b shouldn't be modified when req is in use.
-func (req *Request) Unmarshal(b []byte) error {
-	req.reset()
-	if err := json.Unmarshal(b, req); err != nil {
-		return fmt.Errorf("cannot unmarshal %q: %w", b, err)
-	}
-	// Set missing timestamps to the current time.
-	currentTimestamp := float64(fasttime.UnixTimestamp())
-	series := req.Series
-	for i := range series {
-		points := series[i].Points
-		for j := range points {
-			if points[j][0] <= 0 {
-				points[j][0] = currentTimestamp
-			}
-		}
-	}
-	return nil
-}
-
-// Series represents a series item from DataDog POST request to /api/v1/series
-//
-// See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
-type Series struct {
-	Metric string `json:"metric"`
-	Host   string `json:"host"`
-
-	// The device field does not appear in the datadog docs, but datadog-agent does use it.
-	// Datadog agent (v7 at least), removes the tag "device" and adds it as its own field. Why? That I don't know!
-	// https://github.com/DataDog/datadog-agent/blob/0ada7a97fed6727838a6f4d9c87123d2aafde735/pkg/metrics/series.go#L84-L105
-	Device string `json:"device"`
-
-	// Do not decode Interval, since it isn't used by VictoriaMetrics
-	// Interval int64 `json:"interval"`
-
-	Points []Point  `json:"points"`
-	Tags   []string `json:"tags"`
-
-	// Do not decode Type, since it isn't used by VictoriaMetrics
-	// Type string `json:"type"`
-}
-
-func (s *Series) reset() {
-	s.Metric = ""
-	s.Host = ""
-	s.Device = ""
-
-	points := s.Points
-	for i := range points {
-		points[i] = Point{}
-	}
-	s.Points = points[:0]
-
-	tags := s.Tags
-	for i := range tags {
-		tags[i] = ""
-	}
-	s.Tags = tags[:0]
-}
-
-// Point represents a point from DataDog POST request to /api/v1/series
-type Point [2]float64
-
-// Timestamp returns timestamp in milliseconds from the given pt.
-func (pt *Point) Timestamp() int64 {
-	return int64(pt[0] * 1000)
-}
-
-// Value returns value from the given pt.
-func (pt *Point) Value() float64 {
-	return pt[1]
+type Request interface {
+	Extract(func(prompbmarshal.TimeSeries) error, func(string) string) error
+	Unmarshal([]byte) error
 }
diff --git a/lib/protoparser/datadog/stream/streamparser.go b/lib/protoparser/datadog/stream/streamparser.go
index d79f0b84a..24beb0681 100644
--- a/lib/protoparser/datadog/stream/streamparser.go
+++ b/lib/protoparser/datadog/stream/streamparser.go
@@ -5,6 +5,7 @@ import (
 	"flag"
 	"fmt"
 	"io"
+	"net/http"
 	"regexp"
 	"sync"
 
@@ -12,15 +13,19 @@ import (
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil"
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/common"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog"
+	apiSeriesV1 "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog/api/series/v1"
+	apiSeriesV2 "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog/api/series/v2"
+	apiSketchesBeta "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/datadog/api/sketches/beta"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/writeconcurrencylimiter"
 	"github.com/VictoriaMetrics/metrics"
 )
 
 var (
 	// The maximum request size is defined at https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
-	maxInsertRequestSize = flagutil.NewBytes("datadog.maxInsertRequestSize", 64*1024*1024, "The maximum size in bytes of a single DataDog POST request to /api/v1/series")
+	maxInsertRequestSize = flagutil.NewBytes("datadog.maxInsertRequestSize", 64*1024*1024, "The maximum size in bytes of a single DataDog POST request to /api/v1/series, /api/v2/series, /api/beta/sketches")
 
 	// If all metrics in Datadog have the same naming schema as custom metrics, then the following rules apply:
 	// https://docs.datadoghq.com/metrics/custom_metrics/#naming-custom-metrics
@@ -31,13 +36,15 @@ var (
 		"https://docs.datadoghq.com/metrics/custom_metrics/#naming-custom-metrics")
 )
 
-// Parse parses DataDog POST request for /api/v1/series from reader and calls callback for the parsed request.
+// Parse parses DataDog POST request for /api/v1/series, /api/v2/series, /api/beta/sketches from reader and calls callback for the parsed request.
 //
 // callback shouldn't hold series after returning.
-func Parse(r io.Reader, contentEncoding string, callback func(series []datadog.Series) error) error {
-	wcr := writeconcurrencylimiter.GetReader(r)
+func Parse(req *http.Request, callback func(prompbmarshal.TimeSeries) error) error {
+	var r io.Reader
+	wcr := writeconcurrencylimiter.GetReader(req.Body)
 	defer writeconcurrencylimiter.PutReader(wcr)
 	r = wcr
+	contentEncoding := req.Header.Get("Content-Encoding")
 
 	switch contentEncoding {
 	case "gzip":
@@ -60,25 +67,57 @@ func Parse(r io.Reader, contentEncoding string, callback func(series []datadog.S
 	if err := ctx.Read(); err != nil {
 		return err
 	}
-	req := getRequest()
-	defer putRequest(req)
-	if err := req.Unmarshal(ctx.reqBuf.B); err != nil {
+	apiVersion := insertApisVersionRegex.ReplaceAllString(req.URL.Path, "${version}")
+	apiKind := insertApisVersionRegex.ReplaceAllString(req.URL.Path, "${kind}")
+
+	var ddReq datadog.Request
+	switch apiKind {
+	case "series":
+		switch apiVersion {
+		case "v1":
+			ddReq = getSeriesV1Request()
+			defer putSeriesV1Request(ddReq)
+		case "v2":
+			ddReq = getSeriesV2Request()
+			defer putSeriesV2Request(ddReq)
+		default:
+			return fmt.Errorf(
+				"API version %q of DataDog series endpoint is not supported",
+				apiVersion,
+			)
+		}
+	case "sketches":
+		switch apiVersion {
+		case "beta":
+			ddReq = getSketchesBetaRequest()
+			defer putSketchesBetaRequest(ddReq)
+		default:
+			return fmt.Errorf(
+				"API version %q of DataDog sketches endpoint is not supported",
+				apiVersion,
+			)
+		}
+	default:
+		return fmt.Errorf(
+			"API kind %q of DataDog API is not supported",
+			apiKind,
+		)
+	}
+
+	if err := ddReq.Unmarshal(ctx.reqBuf.B); err != nil {
 		unmarshalErrors.Inc()
 		return fmt.Errorf("cannot unmarshal DataDog POST request with size %d bytes: %w", len(ctx.reqBuf.B), err)
 	}
-	rows := 0
-	series := req.Series
-	for i := range series {
-		rows += len(series[i].Points)
-		if *sanitizeMetricName {
-			series[i].Metric = sanitizeName(series[i].Metric)
-		}
+
+	cb := func(series prompbmarshal.TimeSeries) error {
+		rowsRead.Add(len(series.Samples))
+		return callback(series)
 	}
-	rowsRead.Add(rows)
 
-	if err := callback(series); err != nil {
+	if err := ddReq.Extract(cb, sanitizeName(*sanitizeMetricName)); err != nil {
 		return fmt.Errorf("error when processing imported data: %w", err)
 	}
+
 	return nil
 }
 
@@ -144,25 +183,60 @@ func putPushCtx(ctx *pushCtx) {
 var pushCtxPool sync.Pool
 var pushCtxPoolCh = make(chan *pushCtx, cgroup.AvailableCPUs())
 
-func getRequest() *datadog.Request {
-	v := requestPool.Get()
+func getSeriesV1Request() *apiSeriesV1.Request {
+	v := seriesV1RequestPool.Get()
 	if v == nil {
-		return &datadog.Request{}
+		return &apiSeriesV1.Request{}
 	}
-	return v.(*datadog.Request)
+	return v.(*apiSeriesV1.Request)
 }
 
-func putRequest(req *datadog.Request) {
-	requestPool.Put(req)
+func putSeriesV1Request(req datadog.Request) {
+	seriesV1RequestPool.Put(req)
 }
 
-var requestPool sync.Pool
+var seriesV1RequestPool sync.Pool
+
+func getSeriesV2Request() *apiSeriesV2.Request {
+	v := seriesV2RequestPool.Get()
+	if v == nil {
+		return &apiSeriesV2.Request{}
+	}
+	return v.(*apiSeriesV2.Request)
+}
+
+func putSeriesV2Request(req datadog.Request) {
+	seriesV2RequestPool.Put(req)
+}
+
+var seriesV2RequestPool sync.Pool
+
+func getSketchesBetaRequest() *apiSketchesBeta.Request {
+	v := sketchesBetaRequestPool.Get()
+	if v == nil {
+		return &apiSketchesBeta.Request{}
+	}
+	return v.(*apiSketchesBeta.Request)
+}
+
+func putSketchesBetaRequest(req datadog.Request) {
+	sketchesBetaRequestPool.Put(req)
+}
+
+var sketchesBetaRequestPool sync.Pool
 
 // sanitizeName performs DataDog-compatible sanitizing for metric names
 //
 // See https://docs.datadoghq.com/metrics/custom_metrics/#naming-custom-metrics
-func sanitizeName(name string) string {
-	return namesSanitizer.Transform(name)
+func sanitizeName(sanitize bool) func(string) string {
+	if sanitize {
+		return func(name string) string {
+			return namesSanitizer.Transform(name)
+		}
+	}
+	return func(name string) string {
+		return name
+	}
 }
 
 var namesSanitizer = bytesutil.NewFastStringTransformer(func(s string) string {
@@ -176,4 +250,5 @@ var (
 	unsupportedDatadogChars = regexp.MustCompile(`[^0-9a-zA-Z_\.]+`)
 	multiUnderscores        = regexp.MustCompile(`_+`)
 	underscoresWithDots     = regexp.MustCompile(`_?\._?`)
+	insertApisVersionRegex  = regexp.MustCompile(`.*/api/(?P<version>[\w]+)/(?P<kind>[\w]+)`)
 )
diff --git a/lib/streamaggr/streamaggr.go b/lib/streamaggr/streamaggr.go
index 705709dc7..c3d7bacdd 100644
--- a/lib/streamaggr/streamaggr.go
+++ b/lib/streamaggr/streamaggr.go
@@ -471,22 +471,30 @@ func (a *aggregator) flush() {
 
 		tss := ctx.tss
 
-		// Apply output relabeling
-		if a.outputRelabeling != nil {
-			dst := tss[:0]
-			for _, ts := range tss {
-				ts.Labels = a.outputRelabeling.Apply(ts.Labels, 0)
-				if len(ts.Labels) == 0 {
-					// The metric has been deleted by the relabeling
-					continue
-				}
-				dst = append(dst, ts)
-			}
-			tss = dst
+		if a.outputRelabeling == nil {
+			// Fast path - push the output metrics.
+			a.pushFunc(tss)
+			continue
 		}
 
-		// Push the output metrics.
-		a.pushFunc(tss)
+		// Slower path - apply output relabeling and then push the output metrics.
+		auxLabels := promutils.GetLabels()
+		dstLabels := auxLabels.Labels[:0]
+		dst := tss[:0]
+		for _, ts := range tss {
+			dstLabelsLen := len(dstLabels)
+			dstLabels = append(dstLabels, ts.Labels...)
+			dstLabels = a.outputRelabeling.Apply(dstLabels, dstLabelsLen)
+			if len(dstLabels) == dstLabelsLen {
+				// The metric has been deleted by the relabeling
+				continue
+			}
+			ts.Labels = dstLabels[dstLabelsLen:]
+			dst = append(dst, ts)
+		}
+		a.pushFunc(dst)
+		auxLabels.Labels = dstLabels
+		promutils.PutLabels(auxLabels)
 	}
 }
 
diff --git a/vendor/github.com/VictoriaMetrics/metrics/go_metrics.go b/vendor/github.com/VictoriaMetrics/metrics/go_metrics.go
index f8b606731..5de7cb81d 100644
--- a/vendor/github.com/VictoriaMetrics/metrics/go_metrics.go
+++ b/vendor/github.com/VictoriaMetrics/metrics/go_metrics.go
@@ -3,12 +3,28 @@ package metrics
 import (
 	"fmt"
 	"io"
+	"math"
 	"runtime"
+	runtimemetrics "runtime/metrics"
+	"strings"
 
 	"github.com/valyala/histogram"
 )
 
+// See https://pkg.go.dev/runtime/metrics#hdr-Supported_metrics
+var runtimeMetrics = [][2]string{
+	{"/sched/latencies:seconds", "go_sched_latencies_seconds"},
+	{"/sync/mutex/wait/total:seconds", "go_mutex_wait_seconds_total"},
+	{"/cpu/classes/gc/mark/assist:cpu-seconds", "go_gc_mark_assist_cpu_seconds_total"},
+	{"/cpu/classes/gc/total:cpu-seconds", "go_gc_cpu_seconds_total"},
+	{"/gc/pauses:seconds", "go_gc_pauses_seconds"},
+	{"/cpu/classes/scavenge/total:cpu-seconds", "go_scavenge_cpu_seconds_total"},
+	{"/gc/gomemlimit:bytes", "go_memlimit_bytes"},
+}
+
 func writeGoMetrics(w io.Writer) {
+	writeRuntimeMetrics(w)
+
 	var ms runtime.MemStats
 	runtime.ReadMemStats(&ms)
 	fmt.Fprintf(w, "go_memstats_alloc_bytes %d\n", ms.Alloc)
@@ -17,6 +33,7 @@ func writeGoMetrics(w io.Writer) {
 	fmt.Fprintf(w, "go_memstats_frees_total %d\n", ms.Frees)
 	fmt.Fprintf(w, "go_memstats_gc_cpu_fraction %g\n", ms.GCCPUFraction)
 	fmt.Fprintf(w, "go_memstats_gc_sys_bytes %d\n", ms.GCSys)
+
 	fmt.Fprintf(w, "go_memstats_heap_alloc_bytes %d\n", ms.HeapAlloc)
 	fmt.Fprintf(w, "go_memstats_heap_idle_bytes %d\n", ms.HeapIdle)
 	fmt.Fprintf(w, "go_memstats_heap_inuse_bytes %d\n", ms.HeapInuse)
@@ -62,3 +79,66 @@ func writeGoMetrics(w io.Writer) {
 	fmt.Fprintf(w, "go_info_ext{compiler=%q, GOARCH=%q, GOOS=%q, GOROOT=%q} 1\n",
 		runtime.Compiler, runtime.GOARCH, runtime.GOOS, runtime.GOROOT())
 }
+
+func writeRuntimeMetrics(w io.Writer) {
+	samples := make([]runtimemetrics.Sample, len(runtimeMetrics))
+	for i, rm := range runtimeMetrics {
+		samples[i].Name = rm[0]
+	}
+	runtimemetrics.Read(samples)
+	for i, rm := range runtimeMetrics {
+		writeRuntimeMetric(w, rm[1], &samples[i])
+	}
+}
+
+func writeRuntimeMetric(w io.Writer, name string, sample *runtimemetrics.Sample) {
+	switch sample.Value.Kind() {
+	case runtimemetrics.KindBad:
+		panic(fmt.Errorf("BUG: unexpected runtimemetrics.KindBad for sample.Name=%q", sample.Name))
+	case runtimemetrics.KindUint64:
+		fmt.Fprintf(w, "%s %d\n", name, sample.Value.Uint64())
+	case runtimemetrics.KindFloat64:
+		fmt.Fprintf(w, "%s %g\n", name, sample.Value.Float64())
+	case runtimemetrics.KindFloat64Histogram:
+		writeRuntimeHistogramMetric(w, name, sample.Value.Float64Histogram())
+	}
+}
+
+func writeRuntimeHistogramMetric(w io.Writer, name string, h *runtimemetrics.Float64Histogram) {
+	buckets := h.Buckets
+	counts := h.Counts
+	if len(buckets) != len(counts)+1 {
+		panic(fmt.Errorf("the number of buckets must be bigger than the number of counts by 1 in histogram %s; got buckets=%d, counts=%d", name, len(buckets), len(counts)))
+	}
+	tailCount := uint64(0)
+	if strings.HasSuffix(name, "_seconds") {
+		// Limit the maximum bucket to 1 second, since Go runtime exposes buckets with 10K seconds,
+		// which have little sense. At the same time such buckets may lead to high cardinality issues
+		// at the scraper side.
+		for len(buckets) > 0 && buckets[len(buckets)-1] > 1 {
+			buckets = buckets[:len(buckets)-1]
+			tailCount += counts[len(counts)-1]
+			counts = counts[:len(counts)-1]
+		}
+	}
+
+	iStep := float64(len(buckets)) / maxRuntimeHistogramBuckets
+
+	totalCount := uint64(0)
+	iNext := 0.0
+	for i, count := range counts {
+		totalCount += count
+		if float64(i) >= iNext {
+			iNext += iStep
+			le := buckets[i+1]
+			if !math.IsInf(le, 1) {
+				fmt.Fprintf(w, `%s_bucket{le="%g"} %d`+"\n", name, le, totalCount)
+			}
+		}
+	}
+	totalCount += tailCount
+	fmt.Fprintf(w, `%s_bucket{le="+Inf"} %d`+"\n", name, totalCount)
+}
+
+// Limit the number of buckets for Go runtime histograms in order to prevent from high cardinality issues at scraper side.
+const maxRuntimeHistogramBuckets = 30
diff --git a/vendor/github.com/VictoriaMetrics/metrics/metrics.go b/vendor/github.com/VictoriaMetrics/metrics/metrics.go
index 7dfa97219..e9560b2aa 100644
--- a/vendor/github.com/VictoriaMetrics/metrics/metrics.go
+++ b/vendor/github.com/VictoriaMetrics/metrics/metrics.go
@@ -134,10 +134,26 @@ func WritePrometheus(w io.Writer, exposeProcessMetrics bool) {
 //
 //   - process_io_storage_written_bytes_total - the number of bytes actually written to disk
 //
+//   - go_sched_latencies_seconds - time spent by goroutines in ready state before they start execution
+//
+//   - go_mutex_wait_seconds_total - summary time spent by all the goroutines while waiting for locked mutex
+//
+//   - go_gc_mark_assist_cpu_seconds_total - summary CPU time spent by goroutines in GC mark assist state
+//
+//   - go_gc_cpu_seconds_total - summary time spent in GC
+//
+//   - go_gc_pauses_seconds - duration of GC pauses
+//
+//   - go_scavenge_cpu_seconds_total - CPU time spent on returning the memory to OS
+//
+//   - go_memlimit_bytes - the GOMEMLIMIT env var value
+//
 //   - go_memstats_alloc_bytes - memory usage for Go objects in the heap
 //
 //   - go_memstats_alloc_bytes_total - the cumulative counter for total size of allocated Go objects
 //
+//   - go_memstats_buck_hash_sys_bytes - bytes of memory in profiling bucket hash tables
+//
 //   - go_memstats_frees_total - the cumulative counter for number of freed Go objects
 //
 //   - go_memstats_gc_cpu_fraction - the fraction of CPU spent in Go garbage collector
@@ -148,20 +164,42 @@ func WritePrometheus(w io.Writer, exposeProcessMetrics bool) {
 //
 //   - go_memstats_heap_idle_bytes - idle memory ready for new Go object allocations
 //
+//   - go_memstats_heap_inuse_bytes - bytes in in-use spans
+//
 //   - go_memstats_heap_objects - the number of Go objects in the heap
 //
+//   - go_memstats_heap_released_bytes - bytes of physical memory returned to the OS
+//
 //   - go_memstats_heap_sys_bytes - memory requested for Go objects from the OS
 //
+//   - go_memstats_last_gc_time_seconds - unix timestamp the last garbage collection finished
+//
+//   - go_memstats_lookups_total - the number of pointer lookups performed by the runtime
+//
 //   - go_memstats_mallocs_total - the number of allocations for Go objects
 //
+//   - go_memstats_mcache_inuse_bytes - bytes of allocated mcache structures
+//
+//   - go_memstats_mcache_sys_bytes - bytes of memory obtained from the OS for mcache structures
+//
+//   - go_memstats_mspan_inuse_bytes - bytes of allocated mspan structures
+//
+//   - go_memstats_mspan_sys_bytes - bytes of memory obtained from the OS for mspan structures
+//
 //   - go_memstats_next_gc_bytes - the target heap size when the next garbage collection should start
 //
+//   - go_memstats_other_sys_bytes - bytes of memory in miscellaneous off-heap runtime allocations
+//
 //   - go_memstats_stack_inuse_bytes - memory used for goroutine stacks
 //
 //   - go_memstats_stack_sys_bytes - memory requested fromthe OS for goroutine stacks
 //
 //   - go_memstats_sys_bytes - memory requested by Go runtime from the OS
 //
+//   - go_cgo_calls_count - the total number of CGO calls
+//
+//   - go_cpu_count - the number of CPU cores on the host where the app runs
+//
 // The WriteProcessMetrics func is usually called in combination with writing Set metrics
 // inside "/metrics" handler:
 //
diff --git a/vendor/github.com/VictoriaMetrics/metricsql/transform.go b/vendor/github.com/VictoriaMetrics/metricsql/transform.go
index a7d9f6698..e8d89aa8a 100644
--- a/vendor/github.com/VictoriaMetrics/metricsql/transform.go
+++ b/vendor/github.com/VictoriaMetrics/metricsql/transform.go
@@ -24,6 +24,7 @@ var transformFuncs = map[string]bool{
 	"clamp_min":                  true,
 	"cos":                        true,
 	"cosh":                       true,
+	"day_of_year":                true,
 	"day_of_month":               true,
 	"day_of_week":                true,
 	"days_in_month":              true,
diff --git a/vendor/golang.org/x/sys/unix/fcntl.go b/vendor/golang.org/x/sys/unix/fcntl.go
index 58c6bfc70..6200876fb 100644
--- a/vendor/golang.org/x/sys/unix/fcntl.go
+++ b/vendor/golang.org/x/sys/unix/fcntl.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-//go:build dragonfly || freebsd || linux || netbsd || openbsd
+//go:build dragonfly || freebsd || linux || netbsd
 
 package unix
 
diff --git a/vendor/golang.org/x/sys/unix/ioctl_linux.go b/vendor/golang.org/x/sys/unix/ioctl_linux.go
index 0d12c0851..dbe680eab 100644
--- a/vendor/golang.org/x/sys/unix/ioctl_linux.go
+++ b/vendor/golang.org/x/sys/unix/ioctl_linux.go
@@ -231,3 +231,8 @@ func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) {
 func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error {
 	return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value))
 }
+
+// IoctlLoopConfigure configures all loop device parameters in a single step
+func IoctlLoopConfigure(fd int, value *LoopConfig) error {
+	return ioctlPtr(fd, LOOP_CONFIGURE, unsafe.Pointer(value))
+}
diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh
index cbe24150a..6202638ba 100644
--- a/vendor/golang.org/x/sys/unix/mkerrors.sh
+++ b/vendor/golang.org/x/sys/unix/mkerrors.sh
@@ -519,6 +519,7 @@ ccflags="$@"
 		$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
 		$2 ~ /^LO_(KEY|NAME)_SIZE$/ ||
 		$2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ ||
+		$2 == "LOOP_CONFIGURE" ||
 		$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MREMAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ ||
 		$2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ ||
 		$2 ~ /^NFC_.*_(MAX)?SIZE$/ ||
@@ -560,7 +561,7 @@ ccflags="$@"
 		$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ ||
 		$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
 		$2 ~ /^CLONE_[A-Z_]+/ ||
-		$2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+)$/ &&
+		$2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+|BPF_F_LINK)$/ &&
 		$2 ~ /^(BPF|DLT)_/ ||
 		$2 ~ /^AUDIT_/ ||
 		$2 ~ /^(CLOCK|TIMER)_/ ||
diff --git a/vendor/golang.org/x/sys/unix/syscall_bsd.go b/vendor/golang.org/x/sys/unix/syscall_bsd.go
index 6f328e3a5..a00c3e545 100644
--- a/vendor/golang.org/x/sys/unix/syscall_bsd.go
+++ b/vendor/golang.org/x/sys/unix/syscall_bsd.go
@@ -316,7 +316,7 @@ func GetsockoptString(fd, level, opt int) (string, error) {
 	if err != nil {
 		return "", err
 	}
-	return string(buf[:vallen-1]), nil
+	return ByteSliceToString(buf[:vallen]), nil
 }
 
 //sys	recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go
index a5e1c10e3..0f85e29e6 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux.go
@@ -61,15 +61,23 @@ func FanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname string) (
 }
 
 //sys	fchmodat(dirfd int, path string, mode uint32) (err error)
-
-func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
-	// Linux fchmodat doesn't support the flags parameter. Mimick glibc's behavior
-	// and check the flags. Otherwise the mode would be applied to the symlink
-	// destination which is not what the user expects.
-	if flags&^AT_SYMLINK_NOFOLLOW != 0 {
-		return EINVAL
-	} else if flags&AT_SYMLINK_NOFOLLOW != 0 {
-		return EOPNOTSUPP
+//sys	fchmodat2(dirfd int, path string, mode uint32, flags int) (err error)
+
+func Fchmodat(dirfd int, path string, mode uint32, flags int) error {
+	// Linux fchmodat doesn't support the flags parameter, but fchmodat2 does.
+	// Try fchmodat2 if flags are specified.
+	if flags != 0 {
+		err := fchmodat2(dirfd, path, mode, flags)
+		if err == ENOSYS {
+			// fchmodat2 isn't available. If the flags are known to be valid,
+			// return EOPNOTSUPP to indicate that fchmodat doesn't support them.
+			if flags&^(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 {
+				return EINVAL
+			} else if flags&(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 {
+				return EOPNOTSUPP
+			}
+		}
+		return err
 	}
 	return fchmodat(dirfd, path, mode)
 }
@@ -1302,7 +1310,7 @@ func GetsockoptString(fd, level, opt int) (string, error) {
 			return "", err
 		}
 	}
-	return string(buf[:vallen-1]), nil
+	return ByteSliceToString(buf[:vallen]), nil
 }
 
 func GetsockoptTpacketStats(fd, level, opt int) (*TpacketStats, error) {
diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/vendor/golang.org/x/sys/unix/syscall_openbsd.go
index d2882ee04..b25343c71 100644
--- a/vendor/golang.org/x/sys/unix/syscall_openbsd.go
+++ b/vendor/golang.org/x/sys/unix/syscall_openbsd.go
@@ -166,6 +166,20 @@ func Getresgid() (rgid, egid, sgid int) {
 
 //sys	sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
 
+//sys	fcntl(fd int, cmd int, arg int) (n int, err error)
+//sys	fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) = SYS_FCNTL
+
+// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
+func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
+	return fcntl(int(fd), cmd, arg)
+}
+
+// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
+func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
+	_, err := fcntlPtr(int(fd), cmd, unsafe.Pointer(lk))
+	return err
+}
+
 //sys	ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error)
 
 func Ppoll(fds []PollFd, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris.go b/vendor/golang.org/x/sys/unix/syscall_solaris.go
index 60c8142d4..21974af06 100644
--- a/vendor/golang.org/x/sys/unix/syscall_solaris.go
+++ b/vendor/golang.org/x/sys/unix/syscall_solaris.go
@@ -158,7 +158,7 @@ func GetsockoptString(fd, level, opt int) (string, error) {
 	if err != nil {
 		return "", err
 	}
-	return string(buf[:vallen-1]), nil
+	return ByteSliceToString(buf[:vallen]), nil
 }
 
 const ImplementsGetwd = true
diff --git a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go
index d99d05f1b..b473038c6 100644
--- a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go
+++ b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go
@@ -1104,7 +1104,7 @@ func GetsockoptString(fd, level, opt int) (string, error) {
 		return "", err
 	}
 
-	return string(buf[:vallen-1]), nil
+	return ByteSliceToString(buf[:vallen]), nil
 }
 
 func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go
index 9c00cbf51..c73cfe2f1 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go
@@ -486,7 +486,6 @@ const (
 	BPF_F_ANY_ALIGNMENT                         = 0x2
 	BPF_F_BEFORE                                = 0x8
 	BPF_F_ID                                    = 0x20
-	BPF_F_LINK                                  = 0x2000
 	BPF_F_NETFILTER_IP_DEFRAG                   = 0x1
 	BPF_F_QUERY_EFFECTIVE                       = 0x1
 	BPF_F_REPLACE                               = 0x4
@@ -1802,6 +1801,7 @@ const (
 	LOCK_SH                                     = 0x1
 	LOCK_UN                                     = 0x8
 	LOOP_CLR_FD                                 = 0x4c01
+	LOOP_CONFIGURE                              = 0x4c0a
 	LOOP_CTL_ADD                                = 0x4c80
 	LOOP_CTL_GET_FREE                           = 0x4c82
 	LOOP_CTL_REMOVE                             = 0x4c81
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go
index faca7a557..1488d2712 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go
@@ -37,6 +37,21 @@ func fchmodat(dirfd int, path string, mode uint32) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func fchmodat2(dirfd int, path string, mode uint32, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_FCHMODAT2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func ioctl(fd int, req uint, arg uintptr) (err error) {
 	_, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
 	if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go
index 88bfc2885..a1d061597 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go
@@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func fcntl(fd int, cmd int, arg int) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_fcntl_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
 	r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
 	n = int(r0)
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s
index 4cbeff171..41b561731 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s
@@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
 GLOBL	·libc_sysctl_trampoline_addr(SB), RODATA, $4
 DATA	·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB)
 
+TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
+	JMP	libc_fcntl(SB)
+GLOBL	·libc_fcntl_trampoline_addr(SB), RODATA, $4
+DATA	·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB)
+
 TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
 	JMP	libc_ppoll(SB)
 GLOBL	·libc_ppoll_trampoline_addr(SB), RODATA, $4
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go
index b8a67b99a..5b2a74097 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go
@@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func fcntl(fd int, cmd int, arg int) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_fcntl_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
 	r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
 	n = int(r0)
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s
index 1123f2757..4019a656f 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s
@@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
 GLOBL	·libc_sysctl_trampoline_addr(SB), RODATA, $8
 DATA	·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
 
+TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
+	JMP	libc_fcntl(SB)
+GLOBL	·libc_fcntl_trampoline_addr(SB), RODATA, $8
+DATA	·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB)
+
 TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
 	JMP	libc_ppoll(SB)
 GLOBL	·libc_ppoll_trampoline_addr(SB), RODATA, $8
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go
index af50a65c0..f6eda1344 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go
@@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func fcntl(fd int, cmd int, arg int) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_fcntl_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
 	r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
 	n = int(r0)
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s
index 82badae39..ac4af24f9 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s
@@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
 GLOBL	·libc_sysctl_trampoline_addr(SB), RODATA, $4
 DATA	·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB)
 
+TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
+	JMP	libc_fcntl(SB)
+GLOBL	·libc_fcntl_trampoline_addr(SB), RODATA, $4
+DATA	·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB)
+
 TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
 	JMP	libc_ppoll(SB)
 GLOBL	·libc_ppoll_trampoline_addr(SB), RODATA, $4
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go
index 8fb4ff36a..55df20ae9 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go
@@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func fcntl(fd int, cmd int, arg int) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_fcntl_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
 	r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
 	n = int(r0)
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s
index 24d7eecb9..f77d53212 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s
@@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
 GLOBL	·libc_sysctl_trampoline_addr(SB), RODATA, $8
 DATA	·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
 
+TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
+	JMP	libc_fcntl(SB)
+GLOBL	·libc_fcntl_trampoline_addr(SB), RODATA, $8
+DATA	·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB)
+
 TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
 	JMP	libc_ppoll(SB)
 GLOBL	·libc_ppoll_trampoline_addr(SB), RODATA, $8
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go
index f469a83ee..8c1155cbc 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go
@@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func fcntl(fd int, cmd int, arg int) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_fcntl_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
 	r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
 	n = int(r0)
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s
index 9a498a067..fae140b62 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s
@@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
 GLOBL	·libc_sysctl_trampoline_addr(SB), RODATA, $8
 DATA	·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
 
+TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
+	JMP	libc_fcntl(SB)
+GLOBL	·libc_fcntl_trampoline_addr(SB), RODATA, $8
+DATA	·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB)
+
 TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
 	JMP	libc_ppoll(SB)
 GLOBL	·libc_ppoll_trampoline_addr(SB), RODATA, $8
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go
index c26ca2e1a..7cc80c58d 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go
@@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func fcntl(fd int, cmd int, arg int) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_fcntl_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
 	r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
 	n = int(r0)
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s
index 1f224aa41..9d1e0ff06 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s
@@ -213,6 +213,12 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
 GLOBL	·libc_sysctl_trampoline_addr(SB), RODATA, $8
 DATA	·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
 
+TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
+	CALL	libc_fcntl(SB)
+	RET
+GLOBL	·libc_fcntl_trampoline_addr(SB), RODATA, $8
+DATA	·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB)
+
 TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
 	CALL	libc_ppoll(SB)
 	RET
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go
index bcc920dd2..0688737f4 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go
@@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func fcntl(fd int, cmd int, arg int) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_fcntl_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) {
+	r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
 	r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
 	n = int(r0)
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s
index 87a79c709..da115f9a4 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s
+++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s
@@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
 GLOBL	·libc_sysctl_trampoline_addr(SB), RODATA, $8
 DATA	·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
 
+TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
+	JMP	libc_fcntl(SB)
+GLOBL	·libc_fcntl_trampoline_addr(SB), RODATA, $8
+DATA	·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB)
+
 TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0
 	JMP	libc_ppoll(SB)
 GLOBL	·libc_ppoll_trampoline_addr(SB), RODATA, $8
diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go
index 997bcd55a..bbf8399ff 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_linux.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go
@@ -2671,6 +2671,7 @@ const (
 	BPF_PROG_TYPE_LSM                          = 0x1d
 	BPF_PROG_TYPE_SK_LOOKUP                    = 0x1e
 	BPF_PROG_TYPE_SYSCALL                      = 0x1f
+	BPF_PROG_TYPE_NETFILTER                    = 0x20
 	BPF_CGROUP_INET_INGRESS                    = 0x0
 	BPF_CGROUP_INET_EGRESS                     = 0x1
 	BPF_CGROUP_INET_SOCK_CREATE                = 0x2
@@ -2715,6 +2716,11 @@ const (
 	BPF_PERF_EVENT                             = 0x29
 	BPF_TRACE_KPROBE_MULTI                     = 0x2a
 	BPF_LSM_CGROUP                             = 0x2b
+	BPF_STRUCT_OPS                             = 0x2c
+	BPF_NETFILTER                              = 0x2d
+	BPF_TCX_INGRESS                            = 0x2e
+	BPF_TCX_EGRESS                             = 0x2f
+	BPF_TRACE_UPROBE_MULTI                     = 0x30
 	BPF_LINK_TYPE_UNSPEC                       = 0x0
 	BPF_LINK_TYPE_RAW_TRACEPOINT               = 0x1
 	BPF_LINK_TYPE_TRACING                      = 0x2
@@ -2725,6 +2731,18 @@ const (
 	BPF_LINK_TYPE_PERF_EVENT                   = 0x7
 	BPF_LINK_TYPE_KPROBE_MULTI                 = 0x8
 	BPF_LINK_TYPE_STRUCT_OPS                   = 0x9
+	BPF_LINK_TYPE_NETFILTER                    = 0xa
+	BPF_LINK_TYPE_TCX                          = 0xb
+	BPF_LINK_TYPE_UPROBE_MULTI                 = 0xc
+	BPF_PERF_EVENT_UNSPEC                      = 0x0
+	BPF_PERF_EVENT_UPROBE                      = 0x1
+	BPF_PERF_EVENT_URETPROBE                   = 0x2
+	BPF_PERF_EVENT_KPROBE                      = 0x3
+	BPF_PERF_EVENT_KRETPROBE                   = 0x4
+	BPF_PERF_EVENT_TRACEPOINT                  = 0x5
+	BPF_PERF_EVENT_EVENT                       = 0x6
+	BPF_F_KPROBE_MULTI_RETURN                  = 0x1
+	BPF_F_UPROBE_MULTI_RETURN                  = 0x1
 	BPF_ANY                                    = 0x0
 	BPF_NOEXIST                                = 0x1
 	BPF_EXIST                                  = 0x2
@@ -2742,6 +2760,8 @@ const (
 	BPF_F_MMAPABLE                             = 0x400
 	BPF_F_PRESERVE_ELEMS                       = 0x800
 	BPF_F_INNER_MAP                            = 0x1000
+	BPF_F_LINK                                 = 0x2000
+	BPF_F_PATH_FD                              = 0x4000
 	BPF_STATS_RUN_TIME                         = 0x0
 	BPF_STACK_BUILD_ID_EMPTY                   = 0x0
 	BPF_STACK_BUILD_ID_VALID                   = 0x1
@@ -2762,6 +2782,7 @@ const (
 	BPF_F_ZERO_CSUM_TX                         = 0x2
 	BPF_F_DONT_FRAGMENT                        = 0x4
 	BPF_F_SEQ_NUMBER                           = 0x8
+	BPF_F_NO_TUNNEL_KEY                        = 0x10
 	BPF_F_TUNINFO_FLAGS                        = 0x10
 	BPF_F_INDEX_MASK                           = 0xffffffff
 	BPF_F_CURRENT_CPU                          = 0xffffffff
@@ -2778,6 +2799,8 @@ const (
 	BPF_F_ADJ_ROOM_ENCAP_L4_UDP                = 0x10
 	BPF_F_ADJ_ROOM_NO_CSUM_RESET               = 0x20
 	BPF_F_ADJ_ROOM_ENCAP_L2_ETH                = 0x40
+	BPF_F_ADJ_ROOM_DECAP_L3_IPV4               = 0x80
+	BPF_F_ADJ_ROOM_DECAP_L3_IPV6               = 0x100
 	BPF_ADJ_ROOM_ENCAP_L2_MASK                 = 0xff
 	BPF_ADJ_ROOM_ENCAP_L2_SHIFT                = 0x38
 	BPF_F_SYSCTL_BASE_NAME                     = 0x1
@@ -2866,6 +2889,8 @@ const (
 	BPF_DEVCG_DEV_CHAR                         = 0x2
 	BPF_FIB_LOOKUP_DIRECT                      = 0x1
 	BPF_FIB_LOOKUP_OUTPUT                      = 0x2
+	BPF_FIB_LOOKUP_SKIP_NEIGH                  = 0x4
+	BPF_FIB_LOOKUP_TBID                        = 0x8
 	BPF_FIB_LKUP_RET_SUCCESS                   = 0x0
 	BPF_FIB_LKUP_RET_BLACKHOLE                 = 0x1
 	BPF_FIB_LKUP_RET_UNREACHABLE               = 0x2
@@ -2901,6 +2926,7 @@ const (
 	BPF_CORE_ENUMVAL_EXISTS                    = 0xa
 	BPF_CORE_ENUMVAL_VALUE                     = 0xb
 	BPF_CORE_TYPE_MATCHES                      = 0xc
+	BPF_F_TIMER_ABS                            = 0x1
 )
 
 const (
@@ -2979,6 +3005,12 @@ type LoopInfo64 struct {
 	Encrypt_key      [32]uint8
 	Init             [2]uint64
 }
+type LoopConfig struct {
+	Fd   uint32
+	Size uint32
+	Info LoopInfo64
+	_    [8]uint64
+}
 
 type TIPCSocketAddr struct {
 	Ref  uint32
diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go
index fb6cfd046..47dc57967 100644
--- a/vendor/golang.org/x/sys/windows/syscall_windows.go
+++ b/vendor/golang.org/x/sys/windows/syscall_windows.go
@@ -155,6 +155,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW
 //sys	GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW
 //sys	SetDefaultDllDirectories(directoryFlags uint32) (err error)
+//sys	AddDllDirectory(path *uint16) (cookie uintptr, err error) = kernel32.AddDllDirectory
+//sys	RemoveDllDirectory(cookie uintptr) (err error) = kernel32.RemoveDllDirectory
 //sys	SetDllDirectory(path string) (err error) = kernel32.SetDllDirectoryW
 //sys	GetVersion() (ver uint32, err error)
 //sys	FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW
diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go
index db6282e00..146a1f019 100644
--- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go
+++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go
@@ -184,6 +184,7 @@ var (
 	procGetAdaptersInfo                                      = modiphlpapi.NewProc("GetAdaptersInfo")
 	procGetBestInterfaceEx                                   = modiphlpapi.NewProc("GetBestInterfaceEx")
 	procGetIfEntry                                           = modiphlpapi.NewProc("GetIfEntry")
+	procAddDllDirectory                                      = modkernel32.NewProc("AddDllDirectory")
 	procAssignProcessToJobObject                             = modkernel32.NewProc("AssignProcessToJobObject")
 	procCancelIo                                             = modkernel32.NewProc("CancelIo")
 	procCancelIoEx                                           = modkernel32.NewProc("CancelIoEx")
@@ -330,6 +331,7 @@ var (
 	procReadProcessMemory                                    = modkernel32.NewProc("ReadProcessMemory")
 	procReleaseMutex                                         = modkernel32.NewProc("ReleaseMutex")
 	procRemoveDirectoryW                                     = modkernel32.NewProc("RemoveDirectoryW")
+	procRemoveDllDirectory                                   = modkernel32.NewProc("RemoveDllDirectory")
 	procResetEvent                                           = modkernel32.NewProc("ResetEvent")
 	procResizePseudoConsole                                  = modkernel32.NewProc("ResizePseudoConsole")
 	procResumeThread                                         = modkernel32.NewProc("ResumeThread")
@@ -1605,6 +1607,15 @@ func GetIfEntry(pIfRow *MibIfRow) (errcode error) {
 	return
 }
 
+func AddDllDirectory(path *uint16) (cookie uintptr, err error) {
+	r0, _, e1 := syscall.Syscall(procAddDllDirectory.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
+	cookie = uintptr(r0)
+	if cookie == 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
 func AssignProcessToJobObject(job Handle, process Handle) (err error) {
 	r1, _, e1 := syscall.Syscall(procAssignProcessToJobObject.Addr(), 2, uintptr(job), uintptr(process), 0)
 	if r1 == 0 {
@@ -2879,6 +2890,14 @@ func RemoveDirectory(path *uint16) (err error) {
 	return
 }
 
+func RemoveDllDirectory(cookie uintptr) (err error) {
+	r1, _, e1 := syscall.Syscall(procRemoveDllDirectory.Addr(), 1, uintptr(cookie), 0, 0)
+	if r1 == 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
 func ResetEvent(event Handle) (err error) {
 	r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(event), 0, 0)
 	if r1 == 0 {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index c5af4dd9f..1a606d02c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -97,10 +97,10 @@ github.com/VictoriaMetrics/fastcache
 github.com/VictoriaMetrics/fasthttp
 github.com/VictoriaMetrics/fasthttp/fasthttputil
 github.com/VictoriaMetrics/fasthttp/stackless
-# github.com/VictoriaMetrics/metrics v1.24.0
+# github.com/VictoriaMetrics/metrics v1.25.3
 ## explicit; go 1.20
 github.com/VictoriaMetrics/metrics
-# github.com/VictoriaMetrics/metricsql v0.69.0
+# github.com/VictoriaMetrics/metricsql v0.70.0
 ## explicit; go 1.13
 github.com/VictoriaMetrics/metricsql
 github.com/VictoriaMetrics/metricsql/binaryop
@@ -672,7 +672,7 @@ golang.org/x/oauth2/jwt
 ## explicit; go 1.18
 golang.org/x/sync/errgroup
 golang.org/x/sync/semaphore
-# golang.org/x/sys v0.14.0
+# golang.org/x/sys v0.15.0
 ## explicit; go 1.18
 golang.org/x/sys/cpu
 golang.org/x/sys/unix


Test patch:
diff --git a/app/vmalert-tool/unittest/testdata/test1.yaml b/app/vmalert-tool/unittest/testdata/test1.yaml
index d60e8587f..ab1e75d48 100644
--- a/app/vmalert-tool/unittest/testdata/test1.yaml
+++ b/app/vmalert-tool/unittest/testdata/test1.yaml
@@ -21,6 +21,11 @@ tests:
         groupname: group2
         alertname: SameAlertNameWithDifferentGroup
         exp_alerts: []
+      - eval_time: 150s
+        groupname: group1
+        alertname: SameAlertNameWithDifferentGroup
+        exp_alerts:
+          - {}
       - eval_time: 6m
         groupname: group1
         alertname: SameAlertNameWithDifferentGroup
diff --git a/app/vmalert-tool/unittest/unittest.go b/app/vmalert-tool/unittest/unittest.go
index 424a6132e..fcf4a0019 100644
--- a/app/vmalert-tool/unittest/unittest.go
+++ b/app/vmalert-tool/unittest/unittest.go
@@ -336,7 +336,7 @@ func (tg *testGroup) test(evalInterval time.Duration, groupOrderMap map[string]i
 				if disableAlertgroupLabel {
 					g.Name = ""
 				}
-				if _, ok := alertExpResultMap[time.Duration(ts.UnixNano())][g.Name]; !ok {
+				if _, ok := alertExpResultMap[alertEvalTimes[evalIndex]][g.Name]; !ok {
 					continue
 				}
 				if _, ok := gotAlertsMap[g.Name]; !ok {
@@ -347,7 +347,7 @@ func (tg *testGroup) test(evalInterval time.Duration, groupOrderMap map[string]i
 					if !isAlertRule {
 						continue
 					}
-					if _, ok := alertExpResultMap[time.Duration(ts.UnixNano())][g.Name][ar.Name]; ok {
+					if _, ok := alertExpResultMap[alertEvalTimes[evalIndex]][g.Name][ar.Name]; ok {
 						for _, got := range ar.GetAlerts() {
 							if got.State != notifier.StateFiring {
 								continue
diff --git a/app/vmselect/promql/exec_test.go b/app/vmselect/promql/exec_test.go
index 049789082..20abdebcb 100644
--- a/app/vmselect/promql/exec_test.go
+++ b/app/vmselect/promql/exec_test.go
@@ -853,6 +853,17 @@ func TestExecSuccess(t *testing.T) {
 		resultExpected := []netstorage.Result{r}
 		f(q, resultExpected)
 	})
+	t.Run("day_of_year()", func(t *testing.T) {
+		t.Parallel()
+		q := `day_of_year(time()*1e4)`
+		r := netstorage.Result{
+			MetricName: metricNameExpected,
+			Values:     []float64{116, 139, 163, 186, 209, 232},
+			Timestamps: timestampsExpected,
+		}
+		resultExpected := []netstorage.Result{r}
+		f(q, resultExpected)
+	})
 	t.Run("days_in_month()", func(t *testing.T) {
 		t.Parallel()
 		q := `days_in_month(time()*2e4)`
diff --git a/lib/promrelabel/config_test.go b/lib/promrelabel/config_test.go
index 41f432739..8d4f4340d 100644
--- a/lib/promrelabel/config_test.go
+++ b/lib/promrelabel/config_test.go
@@ -251,6 +251,62 @@ func TestParseRelabelConfigsFailure(t *testing.T) {
 			},
 		})
 	})
+	t.Run("keep_if_contains-missing-target-label", func(t *testing.T) {
+		f([]RelabelConfig{
+			{
+				Action:       "keep_if_contains",
+				SourceLabels: []string{"foo"},
+			},
+		})
+	})
+	t.Run("keep_if_contains-missing-source-labels", func(t *testing.T) {
+		f([]RelabelConfig{
+			{
+				Action:      "keep_if_contains",
+				TargetLabel: "foo",
+			},
+		})
+	})
+	t.Run("keep_if_contains-unused-regex", func(t *testing.T) {
+		f([]RelabelConfig{
+			{
+				Action:       "keep_if_contains",
+				TargetLabel:  "foo",
+				SourceLabels: []string{"bar"},
+				Regex: &MultiLineRegex{
+					S: "bar",
+				},
+			},
+		})
+	})
+	t.Run("drop_if_contains-missing-target-label", func(t *testing.T) {
+		f([]RelabelConfig{
+			{
+				Action:       "drop_if_contains",
+				SourceLabels: []string{"foo"},
+			},
+		})
+	})
+	t.Run("drop_if_contains-missing-source-labels", func(t *testing.T) {
+		f([]RelabelConfig{
+			{
+				Action:      "drop_if_contains",
+				TargetLabel: "foo",
+			},
+		})
+	})
+	t.Run("drop_if_contains-unused-regex", func(t *testing.T) {
+		f([]RelabelConfig{
+			{
+				Action:       "drop_if_contains",
+				TargetLabel:  "foo",
+				SourceLabels: []string{"bar"},
+				Regex: &MultiLineRegex{
+					S: "bar",
+				},
+			},
+		})
+	})
 	t.Run("keep_if_equal-missing-source-labels", func(t *testing.T) {
 		f([]RelabelConfig{
 			{
diff --git a/lib/promrelabel/relabel_test.go b/lib/promrelabel/relabel_test.go
index 0f77b2607..bd2963a51 100644
--- a/lib/promrelabel/relabel_test.go
+++ b/lib/promrelabel/relabel_test.go
@@ -383,6 +383,104 @@ func TestParsedRelabelConfigsApply(t *testing.T) {
   target_label: foo
   replacement: "foobar"
 `, `{}`, true, `{foo="foobar"}`)
+	})
+	t.Run("keep_if_contains-non-existing-target-and-source", func(t *testing.T) {
+		f(`
+- action: keep_if_contains
+  target_label: foo
+  source_labels: [bar]
+`, `{x="y"}`, true, `{x="y"}`)
+	})
+	t.Run("keep_if_contains-non-existing-target", func(t *testing.T) {
+		f(`
+- action: keep_if_contains
+  target_label: foo
+  source_labels: [bar]
+`, `{bar="aaa"}`, true, `{}`)
+	})
+	t.Run("keep_if_contains-non-existing-source", func(t *testing.T) {
+		f(`
+- action: keep_if_contains
+  target_label: foo
+  source_labels: [bar]
+`, `{foo="aaa"}`, true, `{foo="aaa"}`)
+	})
+	t.Run("keep_if_contains-matching-source-target", func(t *testing.T) {
+		f(`
+- action: keep_if_contains
+  target_label: foo
+  source_labels: [bar]
+`, `{bar="aaa",foo="aaa"}`, true, `{bar="aaa",foo="aaa"}`)
+	})
+	t.Run("keep_if_contains-matching-sources-target", func(t *testing.T) {
+		f(`
+- action: keep_if_contains
+  target_label: foo
+  source_labels: [bar, baz]
+`, `{bar="aaa",foo="aaa",baz="aaa"}`, true, `{bar="aaa",baz="aaa",foo="aaa"}`)
+	})
+	t.Run("keep_if_contains-mismatching-source-target", func(t *testing.T) {
+		f(`
+- action: keep_if_contains
+  target_label: foo
+  source_labels: [bar]
+`, `{bar="aaa",foo="bbb"}`, true, `{}`)
+	})
+	t.Run("keep_if_contains-mismatching-sources-target", func(t *testing.T) {
+		f(`
+- action: keep_if_contains
+  target_label: foo
+  source_labels: [bar, baz]
+`, `{bar="aaa",foo="aaa",baz="bbb"}`, true, `{}`)
+	})
+	t.Run("drop_if_contains-non-existing-target-and-source", func(t *testing.T) {
+		f(`
+- action: drop_if_contains
+  target_label: foo
+  source_labels: [bar]
+`, `{x="y"}`, true, `{}`)
+	})
+	t.Run("drop_if_contains-non-existing-target", func(t *testing.T) {
+		f(`
+- action: drop_if_contains
+  target_label: foo
+  source_labels: [bar]
+`, `{bar="aaa"}`, true, `{bar="aaa"}`)
+	})
+	t.Run("drop_if_contains-non-existing-source", func(t *testing.T) {
+		f(`
+- action: drop_if_contains
+  target_label: foo
+  source_labels: [bar]
+`, `{foo="aaa"}`, true, `{}`)
+	})
+	t.Run("drop_if_contains-matching-source-target", func(t *testing.T) {
+		f(`
+- action: drop_if_contains
+  target_label: foo
+  source_labels: [bar]
+`, `{bar="aaa",foo="aaa"}`, true, `{}`)
+	})
+	t.Run("drop_if_contains-matching-sources-target", func(t *testing.T) {
+		f(`
+- action: drop_if_contains
+  target_label: foo
+  source_labels: [bar, baz]
+`, `{bar="aaa",foo="aaa",baz="aaa"}`, true, `{}`)
+	})
+	t.Run("drop_if_contains-mismatching-source-target", func(t *testing.T) {
+		f(`
+- action: drop_if_contains
+  target_label: foo
+  source_labels: [bar]
+`, `{bar="aaa",foo="bbb"}`, true, `{bar="aaa",foo="bbb"}`)
+	})
+	t.Run("drop_if_contains-mismatching-sources-target", func(t *testing.T) {
+		f(`
+- action: drop_if_contains
+  target_label: foo
+  source_labels: [bar, baz]
+`, `{bar="aaa",foo="aaa",baz="bbb"}`, true, `{bar="aaa",baz="bbb",foo="aaa"}`)
 	})
 	t.Run("keep_if_equal-miss", func(t *testing.T) {
 		f(`
diff --git a/lib/protoparser/datadog/api/series/v1/api_test.go b/lib/protoparser/datadog/api/series/v1/api_test.go
new file mode 100644
index 000000000..b218e0665
--- /dev/null
+++ b/lib/protoparser/datadog/api/series/v1/api_test.go
@@ -0,0 +1,88 @@
+package datadog
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
+)
+
+func TestRequestUnmarshalFailure(t *testing.T) {
+	f := func(s string) {
+		t.Helper()
+		req := new(Request)
+		if err := req.Unmarshal([]byte(s)); err == nil {
+			t.Fatalf("expecting non-nil error for Unmarshal(%q)", s)
+		}
+	}
+	f("")
+	f("foobar")
+	f(`{"series":123`)
+	f(`1234`)
+	f(`[]`)
+}
+
+func TestRequestExtract(t *testing.T) {
+	fn := func(s []byte, reqExpected *Request, samplesExp int) {
+		t.Helper()
+		req := new(Request)
+		if err := req.Unmarshal(s); err != nil {
+			t.Fatalf("unexpected error in Unmarshal(%q): %s", s, err)
+		}
+		if !reflect.DeepEqual(req, reqExpected) {
+			t.Fatalf("unexpected row;\ngot\n%+v\nwant\n%+v", req, reqExpected)
+		}
+
+		var samplesTotal int
+		cb := func(ts prompbmarshal.TimeSeries) error {
+			samplesTotal += len(ts.Samples)
+			return nil
+		}
+		sanitizeFn := func(name string) string {
+			return name
+		}
+		if err := req.Extract(cb, sanitizeFn); err != nil {
+			t.Fatalf("error when extracting data: %s", err)
+		}
+
+		if samplesTotal != samplesExp {
+			t.Fatalf("expected to extract %d samples; got %d", samplesExp, samplesTotal)
+		}
+
+	}
+
+	fn([]byte("{}"), new(Request), 0)
+	fn([]byte(`
+{
+  "series": [
+    {
+      "host": "test.example.com",
+      "interval": 20,
+      "metric": "system.load.1",
+      "device": "/dev/sda",
+      "points": [[
+        1575317847,
+        0.5
+      ]],
+      "tags": [
+        "environment:test"
+      ],
+      "type": "rate"
+    }
+  ]
+}
+`), &Request{
+		Series: []series{{
+			Host:   "test.example.com",
+			Metric: "system.load.1",
+			Device: "/dev/sda",
+			Points: []point{{
+				1575317847,
+				0.5,
+			}},
+			Tags: []string{
+				"environment:test",
+			},
+		}},
+	}, 1)
+}
diff --git a/lib/protoparser/datadog/parser_timing_test.go b/lib/protoparser/datadog/api/series/v1/api_timing_test.go
similarity index 97%
rename from lib/protoparser/datadog/parser_timing_test.go
rename to lib/protoparser/datadog/api/series/v1/api_timing_test.go
index f3c2b568a..df37abdc7 100644
--- a/lib/protoparser/datadog/parser_timing_test.go
+++ b/lib/protoparser/datadog/api/series/v1/api_timing_test.go
@@ -26,7 +26,7 @@ func BenchmarkRequestUnmarshal(b *testing.B) {
 	b.SetBytes(int64(len(reqBody)))
 	b.ReportAllocs()
 	b.RunParallel(func(pb *testing.PB) {
-		var req Request
+		req := new(Request)
 		for pb.Next() {
 			if err := req.Unmarshal(reqBody); err != nil {
 				panic(fmt.Errorf("unexpected error: %w", err))
diff --git a/lib/protoparser/datadog/api/series/v2/api_test.go b/lib/protoparser/datadog/api/series/v2/api_test.go
new file mode 100644
index 000000000..4655d2e83
--- /dev/null
+++ b/lib/protoparser/datadog/api/series/v2/api_test.go
@@ -0,0 +1,106 @@
+package datadog
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
+)
+
+func TestRequestUnmarshalFailure(t *testing.T) {
+	f := func(s string) {
+		t.Helper()
+		req := new(Request)
+		if err := req.Unmarshal([]byte(s)); err == nil {
+			t.Fatalf("expecting non-nil error for Unmarshal(%q)", s)
+		}
+	}
+	f("")
+	f("foobar")
+	f(`{"series":123`)
+	f(`1234`)
+	f(`[]`)
+}
+
+func TestRequestExtract(t *testing.T) {
+	fn := func(s []byte, reqExpected *Request, samplesExp int) {
+		t.Helper()
+		req := new(Request)
+		if err := req.Unmarshal(s); err != nil {
+			t.Fatalf("unexpected error in Unmarshal(%q): %s", s, err)
+		}
+		if !reflect.DeepEqual(req, reqExpected) {
+			t.Fatalf("unexpected row;\ngot\n%+v\nwant\n%+v", req, reqExpected)
+		}
+
+		var samplesTotal int
+		cb := func(ts prompbmarshal.TimeSeries) error {
+			samplesTotal += len(ts.Samples)
+			return nil
+		}
+		sanitizeFn := func(name string) string {
+			return name
+		}
+		if err := req.Extract(cb, sanitizeFn); err != nil {
+			t.Fatalf("error when extracting data: %s", err)
+		}
+
+		if samplesTotal != samplesExp {
+			t.Fatalf("expected to extract %d samples; got %d", samplesExp, samplesTotal)
+		}
+
+	}
+	fn([]byte("{}"), new(Request), 0)
+
+	fn([]byte(`
+{
+  "series": [
+    {
+      "interval": 20,
+      "metric": "system.load.1",
+			"resources": [
+				{
+					"name": "test.example.com",
+					"type": "host"
+				}, {
+					"name": "/dev/sda",
+					"type": "device"
+				}
+			],
+			"points": [{
+				"timestamp": 1575317847,
+				"value": 0.5
+			},{
+				"timestamp": 1575317848,
+				"value": 0.6
+			}],
+      "tags": [
+        "environment:test"
+      ],
+      "type": "rate"
+    }
+  ]
+}
+`), &Request{
+		Series: []series{{
+			Metric: "system.load.1",
+			Resources: []resource{{
+				Name: "test.example.com",
+				Type: "host",
+			}, {
+				Name: "/dev/sda",
+				Type: "device",
+			}},
+			Points: []point{{
+				Timestamp: 1575317847,
+				Value:     0.5,
+			}, {
+				Timestamp: 1575317848,
+				Value:     0.6,
+			}},
+			Tags: []string{
+				"environment:test",
+			},
+		}},
+	}, 2)
+}
diff --git a/lib/protoparser/datadog/api/series/v2/api_timing_test.go b/lib/protoparser/datadog/api/series/v2/api_timing_test.go
new file mode 100644
index 000000000..e86448ad0
--- /dev/null
+++ b/lib/protoparser/datadog/api/series/v2/api_timing_test.go
@@ -0,0 +1,44 @@
+package datadog
+
+import (
+	"fmt"
+	"testing"
+)
+
+func BenchmarkRequestUnmarshal(b *testing.B) {
+	reqBody := []byte(`{
+  "series": [
+    {
+      "interval": 20,
+      "metric": "system.load.1",
+			"resources": [{
+				"name": "test.example.com",
+				"type": "host"
+			}],
+      "points": [
+				{
+					"timestamp": 1575317847,
+					"value": 0.5
+				}
+      ],
+      "tags": [
+        "environment:test"
+      ],
+      "type": "rate"
+    }
+  ]
+}`)
+	b.SetBytes(int64(len(reqBody)))
+	b.ReportAllocs()
+	b.RunParallel(func(pb *testing.PB) {
+		req := new(Request)
+		for pb.Next() {
+			if err := req.Unmarshal(reqBody); err != nil {
+				panic(fmt.Errorf("unexpected error: %w", err))
+			}
+			if len(req.Series) != 1 {
+				panic(fmt.Errorf("unexpected number of series unmarshaled: got %d; want 4", len(req.Series)))
+			}
+		}
+	})
+}
diff --git a/lib/protoparser/datadog/parser_test.go b/lib/protoparser/datadog/parser_test.go
index 5ea720331..185cb8b98 100644
--- a/lib/protoparser/datadog/parser_test.go
+++ b/lib/protoparser/datadog/parser_test.go
@@ -1,7 +1,6 @@
 package datadog
 
 import (
-	"reflect"
 	"testing"
 )
 
@@ -21,102 +20,3 @@ func TestSplitTag(t *testing.T) {
 	f("foo:bar", "foo", "bar")
 	f(":bar", "", "bar")
 }
-
-func TestRequestUnmarshalMissingHost(t *testing.T) {
-	// This tests https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3432
-	req := Request{
-		Series: []Series{{
-			Host:   "prev-host",
-			Device: "prev-device",
-		}},
-	}
-	data := `
-{
-  "series": [
-    {
-      "metric": "system.load.1",
-      "points": [[
-        1575317847,
-        0.5
-      ]]
-    }
-  ]
-}`
-	if err := req.Unmarshal([]byte(data)); err != nil {
-		t.Fatalf("unexpected error: %s", err)
-	}
-	reqExpected := Request{
-		Series: []Series{{
-			Metric: "system.load.1",
-			Points: []Point{{
-				1575317847,
-				0.5,
-			}},
-		}},
-	}
-	if !reflect.DeepEqual(&req, &reqExpected) {
-		t.Fatalf("unexpected request parsed;\ngot\n%+v\nwant\n%+v", req, reqExpected)
-	}
-}
-
-func TestRequestUnmarshalFailure(t *testing.T) {
-	f := func(s string) {
-		t.Helper()
-		var req Request
-		if err := req.Unmarshal([]byte(s)); err == nil {
-			t.Fatalf("expecting non-nil error for Unmarshal(%q)", s)
-		}
-	}
-	f("")
-	f("foobar")
-	f(`{"series":123`)
-	f(`1234`)
-	f(`[]`)
-}
-
-func TestRequestUnmarshalSuccess(t *testing.T) {
-	f := func(s string, reqExpected *Request) {
-		t.Helper()
-		var req Request
-		if err := req.Unmarshal([]byte(s)); err != nil {
-			t.Fatalf("unexpected error in Unmarshal(%q): %s", s, err)
-		}
-		if !reflect.DeepEqual(&req, reqExpected) {
-			t.Fatalf("unexpected row;\ngot\n%+v\nwant\n%+v", &req, reqExpected)
-		}
-	}
-	f("{}", &Request{})
-	f(`
-{
-  "series": [
-    {
-      "host": "test.example.com",
-      "interval": 20,
-      "metric": "system.load.1",
-      "device": "/dev/sda",
-      "points": [[
-        1575317847,
-        0.5
-      ]],
-      "tags": [
-        "environment:test"
-      ],
-      "type": "rate"
-    }
-  ]
-}
-`, &Request{
-		Series: []Series{{
-			Host:   "test.example.com",
-			Metric: "system.load.1",
-			Device: "/dev/sda",
-			Points: []Point{{
-				1575317847,
-				0.5,
-			}},
-			Tags: []string{
-				"environment:test",
-			},
-		}},
-	})
-}
diff --git a/lib/protoparser/datadog/stream/streamparser_test.go b/lib/protoparser/datadog/stream/streamparser_test.go
index 19a52edf4..e74d6f253 100644
--- a/lib/protoparser/datadog/stream/streamparser_test.go
+++ b/lib/protoparser/datadog/stream/streamparser_test.go
@@ -7,7 +7,7 @@ import (
 func TestSanitizeName(t *testing.T) {
 	f := func(s, resultExpected string) {
 		t.Helper()
-		result := sanitizeName(s)
+		result := sanitizeName(true)(s)
 		if result != resultExpected {
 			t.Fatalf("unexpected result for sanitizeName(%q); got\n%q\nwant\n%q", s, result, resultExpected)
 		}
diff --git a/lib/streamaggr/streamaggr_test.go b/lib/streamaggr/streamaggr_test.go
index 91ced47e1..33924c8e2 100644
--- a/lib/streamaggr/streamaggr_test.go
+++ b/lib/streamaggr/streamaggr_test.go
@@ -686,6 +686,35 @@ cpu_usage{cpu="2"} 90
 cpu_usage:1m_without_cpu_quantiles{quantile="0.5"} 13.3
 cpu_usage:1m_without_cpu_quantiles{quantile="1"} 90
 `, "1111111")
+
+	// append additional label
+	f(`
+- interval: 1m
+  without: [abc]
+  outputs: [count_samples, sum_samples, count_series]
+  output_relabel_configs:
+  - action: replace_all
+    source_labels: [__name__]
+    regex: ":|_"
+    replacement: "-"
+    target_label: __name__
+  - action: drop
+    source_labels: [de]
+    regex: fg
+  - target_label: new_label
+    replacement: must_keep_metric_name
+`, `
+foo{abc="123"} 4
+bar 5
+foo{abc="123"} 8.5
+foo{abc="456",de="fg"} 8
+`, `bar-1m-without-abc-count-samples{new_label="must_keep_metric_name"} 1
+bar-1m-without-abc-count-series{new_label="must_keep_metric_name"} 1
+bar-1m-without-abc-sum-samples{new_label="must_keep_metric_name"} 5
+foo-1m-without-abc-count-samples{new_label="must_keep_metric_name"} 2
+foo-1m-without-abc-count-series{new_label="must_keep_metric_name"} 1
+foo-1m-without-abc-sum-samples{new_label="must_keep_metric_name"} 12.5
+`, "1111")
 }
 
 func TestAggregatorsWithDedupInterval(t *testing.T) {