diff --git a/.promu.yml b/.promu.yml
index 708cb44307..231833c576 100644
--- a/.promu.yml
+++ b/.promu.yml
@@ -18,7 +18,8 @@ build:
- netgo
- builtinassets
- osusergo
- - stringlabels
+ # - stringlabels
+ - cpplabels
windows:
- builtinassets
- stringlabels
diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go
index d2e8e050a6..ba0d3ccc03 100644
--- a/cmd/prometheus/main.go
+++ b/cmd/prometheus/main.go
@@ -284,6 +284,9 @@ func main() {
runtime.SetMutexProfileFraction(20)
}
+ lssctx, cancelLSS := context.WithCancel(context.Background())
+ labels.Storage.Run(lssctx)
+
var (
oldFlagRetentionDuration model.Duration
newFlagRetentionDuration model.Duration
@@ -790,15 +793,6 @@ func main() {
os.Exit(1)
}
- remoteWriterReadyNotifier := ready.NewNotifiableNotifier()
- remoteWriter := remotewriter.New(
- dataDir,
- headCatalog,
- clock,
- remoteWriterReadyNotifier,
- prometheus.DefaultRegisterer,
- )
-
adapter := pp_pkg_storage.NewAdapter(
clock,
hManager.Proxy(),
@@ -806,7 +800,16 @@ func main() {
hManager.MergeOutOfOrderChunks,
prometheus.DefaultRegisterer,
)
+ labels.Storage.SetAdapter(adapter)
+ remoteWriterReadyNotifier := ready.NewNotifiableNotifier()
+ remoteWriter := remotewriter.New(
+ dataDir,
+ headCatalog,
+ clock,
+ remoteWriterReadyNotifier,
+ prometheus.DefaultRegisterer,
+ )
// PP_CHANGES.md: rebuild on cpp end
var (
@@ -898,6 +901,7 @@ func main() {
scrapeManager, err := scrape.NewManager(
&cfg.scrape,
log.With(logger, "component", "scrape manager"),
+ func(s string) (log.Logger, error) { return logging.NewJSONFileLogger(s) },
adapter,
prometheus.DefaultRegisterer,
)
@@ -1581,6 +1585,8 @@ func main() {
if err := queryEngine.Close(); err != nil {
level.Warn(logger).Log("msg", "Closing query engine failed", "err", err)
}
+
+ cancelLSS()
// PP_CHANGES.md: rebuild on cpp end
level.Info(logger).Log("msg", "See you next time!")
diff --git a/cmd/prometheus/main_test.go b/cmd/prometheus/main_test.go
index c827812e60..060c753586 100644
--- a/cmd/prometheus/main_test.go
+++ b/cmd/prometheus/main_test.go
@@ -147,13 +147,14 @@ func TestSendAlerts(t *testing.T) {
}{
{
in: []*rules.Alert{
- {
- Labels: labels.FromStrings("l1", "v1"),
- Annotations: labels.FromStrings("a2", "v2"),
- ActiveAt: time.Unix(1, 0),
- FiredAt: time.Unix(2, 0),
- ValidUntil: time.Unix(3, 0),
- },
+ rules.NewAlert(
+ labels.FromStrings("l1", "v1"),
+ labels.FromStrings("a2", "v2"),
+ time.Unix(1, 0),
+ time.Unix(2, 0),
+ time.Time{},
+ time.Unix(3, 0),
+ ),
},
exp: []*notifier.Alert{
{
@@ -167,13 +168,14 @@ func TestSendAlerts(t *testing.T) {
},
{
in: []*rules.Alert{
- {
- Labels: labels.FromStrings("l1", "v1"),
- Annotations: labels.FromStrings("a2", "v2"),
- ActiveAt: time.Unix(1, 0),
- FiredAt: time.Unix(2, 0),
- ResolvedAt: time.Unix(4, 0),
- },
+ rules.NewAlert(
+ labels.FromStrings("l1", "v1"),
+ labels.FromStrings("a2", "v2"),
+ time.Unix(1, 0),
+ time.Unix(2, 0),
+ time.Unix(4, 0),
+ time.Time{},
+ ),
},
exp: []*notifier.Alert{
{
@@ -195,7 +197,8 @@ func TestSendAlerts(t *testing.T) {
t.Run(strconv.Itoa(i), func(t *testing.T) {
senderFunc := senderFunc(func(alerts ...*notifier.Alert) {
require.NotEmpty(t, tc.in, "sender called with 0 alert")
- require.Equal(t, tc.exp, alerts)
+ require.True(t, tc.exp[0].Equal(alerts[0]))
+ // require.Equal(t, tc.exp, alerts)
})
rules.SendAlerts(senderFunc, "http://localhost:9090")(context.TODO(), "up", tc.in...)
})
@@ -496,7 +499,7 @@ func TestDocumentation(t *testing.T) {
generatedContent := strings.ReplaceAll(stdout.String(), filepath.Base(promPath), strings.TrimSuffix(filepath.Base(promPath), ".test"))
- expectedContent, err := os.ReadFile(filepath.Join("..", "..", "docs", "command-line", "prometheus.md"))
+ expectedContent, err := os.ReadFile(filepath.Join("..", "..", "docs", "command-line", "prompp.md")) // PP_CHANGES.md: rebuild on cpp
require.NoError(t, err)
require.Equal(t, string(expectedContent), generatedContent, "Generated content does not match documentation. Hint: run `make cli-documentation`.")
diff --git a/cmd/promtool/rules.go b/cmd/promtool/rules.go
index 0ce75e7624..1a5fe2ca58 100644
--- a/cmd/promtool/rules.go
+++ b/cmd/promtool/rules.go
@@ -26,6 +26,7 @@ import (
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/timestamp"
"github.com/prometheus/prometheus/pp-pkg/rules" // PP_CHANGES.md: rebuild on cpp
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/tsdb"
tsdb_errors "github.com/prometheus/prometheus/tsdb/errors"
@@ -71,7 +72,7 @@ func newRuleImporter(logger log.Logger, config ruleImporterConfig, apiClient que
// loadGroups parses groups from a list of recording rule files.
func (importer *ruleImporter) loadGroups(_ context.Context, filenames []string) (errs []error) {
- groups, errs := importer.ruleManager.LoadGroups(importer.config.evalInterval, labels.Labels{}, "", nil, filenames...)
+ groups, errs := importer.ruleManager.LoadGroups(importer.config.evalInterval, cppbridge.Labels{}, "", nil, filenames...) // PP_CHANGES.md: rebuild on cpp
if errs != nil {
return errs
}
diff --git a/cmd/promtool/unittest.go b/cmd/promtool/unittest.go
index d65eba6392..5e09084ce4 100644
--- a/cmd/promtool/unittest.go
+++ b/cmd/promtool/unittest.go
@@ -37,6 +37,7 @@ import (
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/pp-pkg/rules" // PP_CHANGES.md: rebuild on cpp
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/promql/parser"
"github.com/prometheus/prometheus/promql/promqltest"
@@ -192,7 +193,7 @@ type testGroup struct {
InputSeries []series `yaml:"input_series"`
AlertRuleTests []alertTestCase `yaml:"alert_rule_test,omitempty"`
PromqlExprTests []promqlTestCase `yaml:"promql_expr_test,omitempty"`
- ExternalLabels labels.Labels `yaml:"external_labels,omitempty"`
+ ExternalLabels cppbridge.Labels `yaml:"external_labels,omitempty"` // PP_CHANGES.md: rebuild on cpp
ExternalURL string `yaml:"external_url,omitempty"`
TestGroupName string `yaml:"name,omitempty"`
}
@@ -342,8 +343,8 @@ func (tg *testGroup) test(evalInterval time.Duration, groupOrderMap map[string]i
for _, a := range ar.ActiveAlerts() {
if a.State == rules.StateFiring {
alerts = append(alerts, labelAndAnnotation{
- Labels: a.Labels.Copy(),
- Annotations: a.Annotations.Copy(),
+ Labels: a.Labels().Copy(), // PP_CHANGES.md: rebuild on cpp
+ Annotations: a.Annotations().Copy(), // PP_CHANGES.md: rebuild on cpp
})
}
}
diff --git a/config/config.go b/config/config.go
index 039b38adce..6ee5dc92e2 100644
--- a/config/config.go
+++ b/config/config.go
@@ -34,9 +34,9 @@ import (
"gopkg.in/yaml.v2"
"github.com/prometheus/prometheus/discovery"
- "github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/relabel"
pp_pkg_config "github.com/prometheus/prometheus/pp-pkg/config" // PP_CHANGES.md: rebuild on cpp
+ "github.com/prometheus/prometheus/pp/go/cppbridge" // PP_CHANGES.md: rebuild on cpp
"github.com/prometheus/prometheus/storage/remote/azuread"
"github.com/prometheus/prometheus/storage/remote/googleiam"
)
@@ -90,8 +90,8 @@ func Load(s string, expandExternalLabels bool, logger log.Logger) (*Config, erro
return cfg, nil
}
- b := labels.NewScratchBuilder(0)
- cfg.GlobalConfig.ExternalLabels.Range(func(v labels.Label) {
+ b := cppbridge.NewScratchBuilder(cfg.GlobalConfig.ExternalLabels.Len()) // PP_CHANGES.md: rebuild on cpp
+ cfg.GlobalConfig.ExternalLabels.Range(func(v cppbridge.Label) { // PP_CHANGES.md: rebuild on cpp
newV := os.Expand(v.Value, func(s string) string {
if s == "$" {
return "$"
@@ -442,7 +442,7 @@ type GlobalConfig struct {
// File to which scrape failures are logged.
ScrapeFailureLogFile string `yaml:"scrape_failure_log_file,omitempty"`
// The labels to add to any timeseries that this Prometheus instance scrapes.
- ExternalLabels labels.Labels `yaml:"external_labels,omitempty"`
+ ExternalLabels cppbridge.Labels `yaml:"external_labels,omitempty"` // PP_CHANGES.md: rebuild on cpp
// An uncompressed response body larger than this many bytes will cause the
// scrape to fail. 0 means no limit.
BodySizeLimit units.Base2Bytes `yaml:"body_size_limit,omitempty"`
@@ -554,7 +554,7 @@ func (c *GlobalConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
return err
}
- if err := gc.ExternalLabels.Validate(func(l labels.Label) error {
+ if err := gc.ExternalLabels.Validate(func(l cppbridge.Label) error { // PP_CHANGES.md: rebuild on cpp
if !model.LabelName(l.Name).IsValid() {
return fmt.Errorf("%q is not a valid label name", l.Name)
}
diff --git a/config/config_test.go b/config/config_test.go
index 8b5f64b1d3..ae8504a1fb 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -57,8 +57,8 @@ import (
"github.com/prometheus/prometheus/discovery/vultr"
"github.com/prometheus/prometheus/discovery/xds"
"github.com/prometheus/prometheus/discovery/zookeeper"
- "github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/relabel"
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
"github.com/prometheus/prometheus/util/testutil"
)
@@ -89,7 +89,7 @@ var expectedConf = &Config{
QueryLogFile: "testdata/query.log",
ScrapeFailureLogFile: globScrapeFailureLogFile,
- ExternalLabels: labels.FromStrings("foo", "bar", "monitor", "codelab"),
+ ExternalLabels: cppbridge.FromStrings("foo", "bar", "monitor", "codelab"), // PP_CHANGES.md: rebuild on cpp
BodySizeLimit: globBodySizeLimit,
SampleLimit: globSampleLimit,
@@ -2162,16 +2162,16 @@ func TestExpandExternalLabels(t *testing.T) {
c, err := LoadFile("testdata/external_labels.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
- testutil.RequireEqual(t, labels.FromStrings("bar", "foo", "baz", "foo${TEST}bar", "foo", "${TEST}", "qux", "foo$${TEST}", "xyz", "foo$$bar"), c.GlobalConfig.ExternalLabels)
+ testutil.RequireEqual(t, cppbridge.FromStrings("bar", "foo", "baz", "foo${TEST}bar", "foo", "${TEST}", "qux", "foo$${TEST}", "xyz", "foo$$bar"), c.GlobalConfig.ExternalLabels)
c, err = LoadFile("testdata/external_labels.good.yml", false, true, log.NewNopLogger())
require.NoError(t, err)
- testutil.RequireEqual(t, labels.FromStrings("bar", "foo", "baz", "foobar", "foo", "", "qux", "foo${TEST}", "xyz", "foo$bar"), c.GlobalConfig.ExternalLabels)
+ testutil.RequireEqual(t, cppbridge.FromStrings("bar", "foo", "baz", "foobar", "foo", "", "qux", "foo${TEST}", "xyz", "foo$bar"), c.GlobalConfig.ExternalLabels)
os.Setenv("TEST", "TestValue")
c, err = LoadFile("testdata/external_labels.good.yml", false, true, log.NewNopLogger())
require.NoError(t, err)
- testutil.RequireEqual(t, labels.FromStrings("bar", "foo", "baz", "fooTestValuebar", "foo", "TestValue", "qux", "foo${TEST}", "xyz", "foo$bar"), c.GlobalConfig.ExternalLabels)
+ testutil.RequireEqual(t, cppbridge.FromStrings("bar", "foo", "baz", "fooTestValuebar", "foo", "TestValue", "qux", "foo${TEST}", "xyz", "foo$bar"), c.GlobalConfig.ExternalLabels)
}
func TestAgentMode(t *testing.T) {
diff --git a/config/pp_config_test.go b/config/pp_config_test.go
index 109e6a14d0..e7a45f466a 100644
--- a/config/pp_config_test.go
+++ b/config/pp_config_test.go
@@ -15,7 +15,6 @@ import (
"gopkg.in/yaml.v2"
"github.com/prometheus/prometheus/config"
- "github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/relabel"
pp_pkg_config "github.com/prometheus/prometheus/pp-pkg/config"
"github.com/prometheus/prometheus/pp/go/cppbridge"
@@ -171,7 +170,7 @@ var expectedConf = &config.Config{
EvaluationInterval: model.Duration(30 * time.Second),
QueryLogFile: "",
- ExternalLabels: labels.FromStrings("foo", "bar", "monitor", "codelab"),
+ ExternalLabels: cppbridge.FromMap(map[string]string{"foo": "bar", "monitor": "codelab"}),
BodySizeLimit: 15 * units.MiB,
SampleLimit: 1500,
diff --git a/discovery/kubernetes/pod_test.go b/discovery/kubernetes/pod_test.go
index 286a1a230d..2d73b3cd30 100644
--- a/discovery/kubernetes/pod_test.go
+++ b/discovery/kubernetes/pod_test.go
@@ -208,6 +208,7 @@ func expectedPodTargetGroups(ns string) map[string]*targetgroup.Group {
"__meta_kubernetes_pod_container_port_protocol": "TCP",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6",
+ "__sample_limit__": "", // PP_CHANGES.md: sample limit with annotation
},
},
Labels: model.LabelSet{
@@ -233,6 +234,9 @@ func expectedPodTargetGroupsWithNodeMeta(ns, nodeName string, nodeLabels map[str
tg.Labels[model.LabelName("__meta_kubernetes_node_label_"+k)] = lv(v)
tg.Labels[model.LabelName("__meta_kubernetes_node_labelpresent_"+k)] = lv("true")
}
+ for _, target := range tg.Targets {
+ target["__sample_limit__"] = lv("") // PP_CHANGES.md: override sample limit with annotation
+ }
}
return result
@@ -260,6 +264,7 @@ func TestPodDiscoveryBeforeRun(t *testing.T) {
"__meta_kubernetes_pod_container_port_protocol": "TCP",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6",
+ "__sample_limit__": "", // PP_CHANGES.md: sample limit with ann
},
{
"__address__": "1.2.3.4:9001",
@@ -270,6 +275,7 @@ func TestPodDiscoveryBeforeRun(t *testing.T) {
"__meta_kubernetes_pod_container_port_protocol": "UDP",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6",
+ "__sample_limit__": "", // PP_CHANGES.md: sample limit with ann
},
{
"__address__": "1.2.3.4",
@@ -277,6 +283,7 @@ func TestPodDiscoveryBeforeRun(t *testing.T) {
"__meta_kubernetes_pod_container_image": "testcontainer1:latest",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "containerd://6f5e4d3c2b1a",
+ "__sample_limit__": "", // PP_CHANGES.md: sample limit with annotation
},
},
Labels: model.LabelSet{
@@ -313,6 +320,7 @@ func TestPodDiscoveryInitContainer(t *testing.T) {
"__meta_kubernetes_pod_container_image": "initcontainer:latest",
"__meta_kubernetes_pod_container_init": "true",
"__meta_kubernetes_pod_container_id": "containerd://6f5e4d3c2b1a",
+ "__sample_limit__": "", // PP_CHANGES.md: sample limit with annotation
})
expected[key].Labels["__meta_kubernetes_pod_phase"] = "Pending"
expected[key].Labels["__meta_kubernetes_pod_ready"] = "false"
diff --git a/discovery/kubernetes/service_test.go b/discovery/kubernetes/service_test.go
index dde3aaea57..02735eaaf3 100644
--- a/discovery/kubernetes/service_test.go
+++ b/discovery/kubernetes/service_test.go
@@ -141,6 +141,7 @@ func TestServiceDiscoveryAdd(t *testing.T) {
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport",
"__meta_kubernetes_service_port_number": "30900",
+ "__sample_limit__": "", // PP_CHANGES.md: override sample limit
},
},
Labels: model.LabelSet{
@@ -158,6 +159,7 @@ func TestServiceDiscoveryAdd(t *testing.T) {
"__meta_kubernetes_service_port_name": "testport",
"__meta_kubernetes_service_port_number": "31900",
"__meta_kubernetes_service_external_name": "FooExternalName",
+ "__sample_limit__": "", // PP_CHANGES.md: override sample limit
},
},
Labels: model.LabelSet{
@@ -176,6 +178,7 @@ func TestServiceDiscoveryAdd(t *testing.T) {
"__meta_kubernetes_service_port_number": "31900",
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_loadbalancer_ip": "127.0.0.1",
+ "__sample_limit__": "", // PP_CHANGES.md: override sample limit
},
},
Labels: model.LabelSet{
@@ -226,6 +229,7 @@ func TestServiceDiscoveryUpdate(t *testing.T) {
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport0",
"__meta_kubernetes_service_port_number": "30900",
+ "__sample_limit__": "", // PP_CHANGES.md: override sample limit
},
{
"__meta_kubernetes_service_port_protocol": "UDP",
@@ -234,6 +238,7 @@ func TestServiceDiscoveryUpdate(t *testing.T) {
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport1",
"__meta_kubernetes_service_port_number": "30901",
+ "__sample_limit__": "", // PP_CHANGES.md: override sample limit
},
},
Labels: model.LabelSet{
@@ -273,6 +278,7 @@ func TestServiceDiscoveryNamespaces(t *testing.T) {
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport",
"__meta_kubernetes_service_port_number": "30900",
+ "__sample_limit__": "", // PP_CHANGES.md: override sample limit
},
},
Labels: model.LabelSet{
@@ -290,6 +296,7 @@ func TestServiceDiscoveryNamespaces(t *testing.T) {
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport",
"__meta_kubernetes_service_port_number": "30900",
+ "__sample_limit__": "", // PP_CHANGES.md: override sample limit
},
},
Labels: model.LabelSet{
@@ -325,6 +332,7 @@ func TestServiceDiscoveryOwnNamespace(t *testing.T) {
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport",
"__meta_kubernetes_service_port_number": "30900",
+ "__sample_limit__": "", // PP_CHANGES.md: override sample limit
},
},
Labels: model.LabelSet{
@@ -360,6 +368,7 @@ func TestServiceDiscoveryAllNamespaces(t *testing.T) {
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport",
"__meta_kubernetes_service_port_number": "30900",
+ "__sample_limit__": "", // PP_CHANGES.md: override sample limit
},
},
Labels: model.LabelSet{
@@ -377,6 +386,7 @@ func TestServiceDiscoveryAllNamespaces(t *testing.T) {
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport",
"__meta_kubernetes_service_port_number": "30900",
+ "__sample_limit__": "", // PP_CHANGES.md: override sample limit
},
},
Labels: model.LabelSet{
diff --git a/docs/command-line/prompp.md b/docs/command-line/prompp.md
new file mode 100644
index 0000000000..adaca2ff30
--- /dev/null
+++ b/docs/command-line/prompp.md
@@ -0,0 +1,67 @@
+---
+title: prometheus
+---
+
+# prometheus
+
+The Prom++ monitoring server
+
+
+
+## Flags
+
+| Flag | Description | Default |
+| --- | --- | --- |
+| -h, --help | Show context-sensitive help (also try --help-long and --help-man). | |
+| --version | Show application version. | |
+| --config.file | Prometheus configuration file path. | `prometheus.yml` |
+| --web.listen-address ... | Address to listen on for UI, API, and telemetry. Can be repeated. | `0.0.0.0:9090` |
+| --auto-gomemlimit.ratio | The ratio of reserved GOMEMLIMIT memory to the detected maximum container or system memory | `0.9` |
+| --web.config.file | [EXPERIMENTAL] Path to configuration file that can enable TLS or authentication. | |
+| --web.read-timeout | Maximum duration before timing out read of the request, and closing idle connections. | `5m` |
+| --web.max-connections | Maximum number of simultaneous connections across all listeners. | `512` |
+| --web.external-url | The URL under which Prometheus is externally reachable (for example, if Prometheus is served via a reverse proxy). Used for generating relative and absolute links back to Prometheus itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Prometheus. If omitted, relevant URL components will be derived automatically. | |
+| --web.route-prefix | Prefix for the internal routes of web endpoints. Defaults to path of --web.external-url. | |
+| --web.user-assets | Path to static asset directory, available at /user. | |
+| --web.enable-lifecycle | Enable shutdown and reload via HTTP request. | `false` |
+| --web.enable-admin-api | Enable API endpoints for admin control actions. | `false` |
+| --web.enable-remote-write-receiver | Enable API endpoint accepting remote write requests. | `false` |
+| --web.remote-write-receiver.accepted-protobuf-messages | List of the remote write protobuf messages to accept when receiving the remote writes. Supported values: prometheus.WriteRequest, io.prometheus.write.v2.Request | `prometheus.WriteRequest` |
+| --web.console.templates | Path to the console template directory, available at /consoles. | `consoles` |
+| --web.console.libraries | Path to the console library directory. | `console_libraries` |
+| --web.page-title | Document title of Prom++ instance. | `Prom++ Time Series Collection and Processing Server` |
+| --web.cors.origin | Regex for CORS origin. It is fully anchored. Example: 'https?://(domain1\|domain2)\.com' | `.*` |
+| --storage.tsdb.path | Base path for metrics storage. Use with server mode only. | `data/` |
+| --storage.tsdb.retention | [DEPRECATED] How long to retain samples in storage. This flag has been deprecated, use "storage.tsdb.retention.time" instead. Use with server mode only. | |
+| --storage.tsdb.retention.time | How long to retain samples in storage. When this flag is set it overrides "storage.tsdb.retention". If neither this flag nor "storage.tsdb.retention" nor "storage.tsdb.retention.size" is set, the retention time defaults to 15d. Units Supported: y, w, d, h, m, s, ms. Use with server mode only. | |
+| --storage.tsdb.retention.size | Maximum number of bytes that can be stored for blocks. A unit is required, supported units: B, KB, MB, GB, TB, PB, EB. Ex: "512MB". Based on powers-of-2, so 1KB is 1024B. Use with server mode only. | |
+| --storage.tsdb.no-lockfile | Do not create lockfile in data directory. Use with server mode only. | `false` |
+| --storage.wal-commit-interval | Interval between force commits. Use with server mode only. | `5000ms` |
+| --storage.wal-max-samples_per_segment | Maximum samples in WAL segment. Use with server mode only. | `100000` |
+| --storage.head-retention-timeout | Timeout before inactive heads are shrieked. Use with server mode only. | `5m` |
+| --storage.tsdb.head-chunks-write-queue-size | Size of the queue through which head chunks are written to the disk to be m-mapped, 0 disables the queue completely. Experimental. Use with server mode only. | `0` |
+| --storage.agent.path | Base path for metrics storage. Use with agent mode only. | `data-agent/` |
+| --storage.agent.wal-compression | Compress the agent WAL. Use with agent mode only. | `true` |
+| --storage.agent.retention.min-time | Minimum age samples may be before being considered for deletion when the WAL is truncated Use with agent mode only. | |
+| --storage.agent.retention.max-time | Maximum age samples may be before being forcibly deleted when the WAL is truncated Use with agent mode only. | |
+| --storage.agent.no-lockfile | Do not create lockfile in data directory. Use with agent mode only. | `false` |
+| --storage.remote.flush-deadline | How long to wait flushing sample on shutdown or config reload. | `1m` |
+| --storage.remote.read-sample-limit | Maximum overall number of samples to return via the remote read interface, in a single query. 0 means no limit. This limit is ignored for streamed response types. Use with server mode only. | `5e7` |
+| --storage.remote.read-concurrent-limit | Maximum number of concurrent remote read calls. 0 means no limit. Use with server mode only. | `10` |
+| --storage.remote.read-max-bytes-in-frame | Maximum number of bytes in a single frame for streaming remote read response types before marshalling. Note that client might have limit on frame size as well. 1MB as recommended by protobuf by default. Use with server mode only. | `1048576` |
+| --rules.alert.for-outage-tolerance | Max time to tolerate prometheus outage for restoring "for" state of alert. Use with server mode only. | `1h` |
+| --rules.alert.for-grace-period | Minimum duration between alert and restored "for" state. This is maintained only for alerts with configured "for" time greater than grace period. Use with server mode only. | `10m` |
+| --rules.alert.resend-delay | Minimum amount of time to wait before resending an alert to Alertmanager. Use with server mode only. | `1m` |
+| --rules.max-concurrent-evals | Global concurrency limit for independent rules that can run concurrently. When set, "query.max-concurrency" may need to be adjusted accordingly. Use with server mode only. | `4` |
+| --alertmanager.notification-queue-capacity | The capacity of the queue for pending Alertmanager notifications. Use with server mode only. | `10000` |
+| --alertmanager.drain-notification-queue-on-shutdown | Send any outstanding Alertmanager notifications when shutting down. If false, any outstanding Alertmanager notifications will be dropped when shutting down. Use with server mode only. | `true` |
+| --query.lookback-delta | The maximum lookback duration for retrieving metrics during expression evaluations and federation. Use with server mode only. | `5m` |
+| --query.timeout | Maximum time a query may take before being aborted. Use with server mode only. | `2m` |
+| --query.max-concurrency | Maximum number of queries executed concurrently. Use with server mode only. | `20` |
+| --query.max-samples | Maximum number of samples a single query can load into memory. Note that queries will fail if they try to load more samples than this into memory, so this also limits the number of samples a query can return. Use with server mode only. | `50000000` |
+| --scrape.name-escaping-scheme | Method for escaping legacy invalid names when sending to Prometheus that does not support UTF-8. Can be one of "values", "underscores", or "dots". | `values` |
+| --enable-feature ... | Comma separated feature names to enable. Valid options: agent, auto-gomaxprocs, auto-gomemlimit, concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, expand-external-labels, extra-scrape-metrics, memory-snapshot-on-shutdown, native-histograms, new-service-discovery-manager, no-default-scrape-port, otlp-write-receiver, promql-experimental-functions, promql-delayed-name-removal, promql-per-step-stats, remote-write-receiver (DEPRECATED), utf8-names. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details. | |
+| --log.level | Only log messages with the given severity or above. One of: [debug, info, warn, error] | `info` |
+| --log.format | Output format of log messages. One of: [logfmt, json] | `logfmt` |
+
+
diff --git a/go.mod b/go.mod
index 2d20d2b37b..0a2917ba30 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0
github.com/Code-Hex/go-generics-cache v1.5.1
github.com/KimMachineGun/automemlimit v0.7.4
+ github.com/RoaringBitmap/roaring/v2 v2.14.4
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30
github.com/aws/aws-sdk-go v1.55.5
@@ -64,7 +65,7 @@ require (
github.com/prometheus/exporter-toolkit v0.12.0
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c
- github.com/stretchr/testify v1.10.0
+ github.com/stretchr/testify v1.11.1
github.com/vultr/govultr/v2 v2.17.2
go.opentelemetry.io/collector/pdata v1.14.1
go.opentelemetry.io/collector/semconv v0.108.1
@@ -129,6 +130,7 @@ require (
github.com/ashanbrown/forbidigo v1.6.0 // indirect
github.com/ashanbrown/makezero v1.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
+ github.com/bits-and-blooms/bitset v1.24.2 // indirect
github.com/bkielbasa/cyclop v1.2.3 // indirect
github.com/blizzy78/varnamelen v0.8.0 // indirect
github.com/bombsimon/wsl/v4 v4.5.0 // indirect
@@ -271,6 +273,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/moricho/tparallel v0.3.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
+ github.com/mschoch/smat v0.2.0 // indirect
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nishanths/exhaustive v0.12.0 // indirect
github.com/nishanths/predeclared v0.2.2 // indirect
diff --git a/go.sum b/go.sum
index 4c128a6b67..a92fc5969d 100644
--- a/go.sum
+++ b/go.sum
@@ -96,6 +96,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4=
github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo=
+github.com/RoaringBitmap/roaring/v2 v2.14.4 h1:4aKySrrg9G/5oRtJ3TrZLObVqxgQ9f1znCRBwEwjuVw=
+github.com/RoaringBitmap/roaring/v2 v2.14.4/go.mod h1:oMvV6omPWr+2ifRdeZvVJyaz+aoEUopyv5iH0u/+wbY=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
@@ -154,6 +156,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bits-and-blooms/bitset v1.24.2 h1:M7/NzVbsytmtfHbumG+K2bremQPMJuqv1JD3vOaFxp0=
+github.com/bits-and-blooms/bitset v1.24.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w=
github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo=
@@ -797,6 +801,8 @@ github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKH
github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
+github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -1052,8 +1058,8 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
diff --git a/model/labels/labels.go b/model/labels/labels.go
index f4de7496ce..7b8642dc03 100644
--- a/model/labels/labels.go
+++ b/model/labels/labels.go
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//go:build !stringlabels && !dedupelabels
+//go:build !stringlabels && !dedupelabels && !cpplabels
package labels
diff --git a/model/labels/labels_common_noncpp.go b/model/labels/labels_common_noncpp.go
index f51fdb9583..82abee756e 100644
--- a/model/labels/labels_common_noncpp.go
+++ b/model/labels/labels_common_noncpp.go
@@ -3,18 +3,19 @@
package labels
import (
+ "context"
"strings"
"github.com/prometheus/prometheus/pp/go/cppbridge"
)
// NewLabelsWithLSS init LabelsCpp with LabelSetSnapshot and ls id.
-func NewLabelsWithLSS(lss *cppbridge.LabelSetSnapshot, id uint32, builder *ScratchBuilder) Labels {
+func NewLabelsWithLSS(lss *cppbridge.LabelSetSnapshot, builder *ScratchBuilder, id uint32, _ uint16) Labels {
if lss == nil {
return EmptyLabels()
}
- _ = lss.RangeLabelSet(id, func(l cppbridge.Label) error {
+ _ = lss.RangeLabelSet(id, false, func(l cppbridge.Label) error {
// copy string from cpp memory
builder.Add(strings.Clone(l.Name), strings.Clone(l.Value))
@@ -23,3 +24,37 @@ func NewLabelsWithLSS(lss *cppbridge.LabelSetSnapshot, id uint32, builder *Scrat
return builder.Labels()
}
+
+// RenewSnapshot renew ls snapshot. Implementation cpplabels.
+func (*Labels) RenewSnapshot() {
+ // no-op
+}
+
+//
+// ScratchBuilder
+//
+
+// SetSkipCache set the flag to ignore caches. Implementation cpplabels.
+func (*ScratchBuilder) SetSkipCache() {
+ // no-op
+}
+
+//
+// Storage
+//
+
+// Storage global label set storage. Implementation cpplabels.
+var Storage = &noopStorage{}
+
+// noopStorage for label set. Implementation cpplabels.
+type noopStorage struct{}
+
+// SetAdapter store [Adapter]. Implementation cpplabels.
+func (*noopStorage) SetAdapter(any) {
+ // no-op
+}
+
+// Run starts goroutine of the metric collector and the cleaner.
+func (*noopStorage) Run(context.Context) {
+ // no-op
+}
diff --git a/model/labels/labels_common_nonslice_noncpp.go b/model/labels/labels_common_nonslice_noncpp.go
index c6dd5bc3df..3ef70e84ab 100644
--- a/model/labels/labels_common_nonslice_noncpp.go
+++ b/model/labels/labels_common_nonslice_noncpp.go
@@ -2,15 +2,19 @@
package labels
-import "github.com/prometheus/prometheus/pp/go/cppbridge"
+import (
+ "context"
+
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
+)
// NewLabelsWithLSS init LabelsCpp with LabelSetSnapshot and ls id.
-func NewLabelsWithLSS(lss *cppbridge.LabelSetSnapshot, id uint32, builder *ScratchBuilder) Labels {
+func NewLabelsWithLSS(lss *cppbridge.LabelSetSnapshot, builder *ScratchBuilder, id uint32, _ uint16) Labels {
if lss == nil {
return EmptyLabels()
}
- _ = lss.RangeLabelSet(id, func(l cppbridge.Label) error {
+ _ = lss.RangeLabelSet(id, false, func(l cppbridge.Label) error {
builder.Add(l.Name, l.Value)
return nil
@@ -18,3 +22,37 @@ func NewLabelsWithLSS(lss *cppbridge.LabelSetSnapshot, id uint32, builder *Scrat
return builder.Labels()
}
+
+// RenewSnapshot renew ls snapshot. Implementation cpplabels.
+func (*Labels) RenewSnapshot() {
+ // no-op
+}
+
+//
+// ScratchBuilder
+//
+
+// SetSkipCache set the flag to ignore caches. Implementation cpplabels.
+func (*ScratchBuilder) SetSkipCache() {
+ // no-op
+}
+
+//
+// Storage
+//
+
+// Storage global label set storage. Implementation cpplabels.
+var Storage = &noopStorage{}
+
+// noopStorage for label set. Implementation cpplabels.
+type noopStorage struct{}
+
+// SetAdapter store [Adapter]. Implementation cpplabels.
+func (*noopStorage) SetAdapter(any) {
+ // no-op
+}
+
+// Run starts goroutine of the metric collector and the cleaner.
+func (*noopStorage) Run(context.Context) {
+ // no-op
+}
diff --git a/model/labels/labels_cpp.go b/model/labels/labels_cpp.go
new file mode 100644
index 0000000000..8012515c96
--- /dev/null
+++ b/model/labels/labels_cpp.go
@@ -0,0 +1,827 @@
+//go:build cpplabels
+
+package labels
+
+import (
+ "context"
+ "slices"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+ "unsafe"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "go.uber.org/atomic"
+
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
+ "github.com/prometheus/prometheus/pp/go/util"
+)
+
+// Labels is a sorted set of labels. Is implemented by a cpp lss.
+type Labels struct {
+ snapshot *cppbridge.LabelSetSnapshot
+ id uint32
+ length uint16
+ dropMetricName bool
+}
+
+// EmptyLabels returns n null Labels value, for convenience.
+func EmptyLabels() Labels {
+ return Labels{}
+}
+
+// NewLabelsCppWithLSS init LabelsCpp with LabelSetStorage and ls id.
+func NewLabelsWithLSS(lss *cppbridge.LabelSetSnapshot, _ *ScratchBuilder, id uint32, length uint16) Labels {
+ return Labels{
+ snapshot: lss,
+ id: id,
+ length: length,
+ }
+}
+
+// New returns a sorted Labels from the given labels.
+// The caller has to guarantee that all label names are unique.
+func New(ls ...Label) Labels {
+ builder := NewScratchBuilder(len(ls))
+ for _, l := range ls {
+ builder.Add(l.Name, l.Value)
+ }
+
+ return builder.Labels()
+}
+
+// FromStrings creates new labels from pairs of strings.
+func FromStrings(ss ...string) Labels {
+ if len(ss)%2 != 0 { //revive:disable-line:add-constant // not need const
+ panic("invalid number of strings")
+ }
+
+ builder := NewScratchBuilder(len(ss) / 2)
+ for i := 0; i < len(ss); i += 2 { //revive:disable-line:add-constant // not need const
+ builder.Add(ss[i], ss[i+1])
+ }
+
+ return builder.Labels()
+}
+
+// Bytes returns ls as a byte slice.
+// It uses an byte invalid character as a separator and so should not be used for printing.
+func (ls Labels) Bytes(buf []byte) []byte {
+ buf = buf[:0]
+ if ls.IsZero() {
+ return append(buf, labelSep)
+ }
+
+ return ls.snapshot.LabelSetBytes(ls.id, buf, ls.dropMetricName)
+}
+
+// BytesWithLabels is just as Bytes(), but only for labels matching names.
+// 'names' have to be sorted in ascending order.
+func (ls Labels) BytesWithLabels(buf []byte, names ...string) []byte {
+ buf = buf[:0]
+ if ls.IsZero() || len(names) == 0 {
+ return append(buf, labelSep)
+ }
+
+ return ls.snapshot.LabelSetBytesWithLabels(ls.id, buf, ls.dropMetricName, names)
+}
+
+// BytesWithoutLabels is just as Bytes(), but only for labels not matching names.
+// 'names' have to be sorted in ascending order.
+func (ls Labels) BytesWithoutLabels(buf []byte, names ...string) []byte {
+ buf = buf[:0]
+ if ls.IsZero() {
+ return append(buf, labelSep)
+ }
+
+ return ls.snapshot.LabelSetBytesWithoutLabels(ls.id, buf, ls.dropMetricName, names)
+}
+
+// Copy returns a copy of the labels.
+func (ls Labels) Copy() Labels {
+ // labelset is immutable
+ return ls
+}
+
+// CopyFrom copy labels from b on top of whatever was in ls previously, reusing memory or expanding if needed.
+func (ls *Labels) CopyFrom(b Labels) {
+ // tsdb/index/index.go 535
+ *ls = b
+}
+
+// DropMetricName returns Labels with "__name__" removed.
+func (ls Labels) DropMetricName() Labels {
+ if ls.IsZero() {
+ return ls
+ }
+
+ ls.dropMetricName = true
+ ls.length = uint16(ls.snapshot.LabelSetLength(ls.id, ls.dropMetricName))
+
+ return ls
+}
+
+// Get returns the value for the label with the given name.
+// Returns an empty string if the label doesn't exist.
+func (ls Labels) Get(name string) string {
+ if name == "" { // Avoid crash in loop if someone asks for "".
+ return "" // Prometheus does not store blank label names.
+ }
+
+ if ls.IsZero() {
+ return ""
+ }
+
+ if ls.dropMetricName && name == MetricName {
+ return ""
+ }
+
+ return ls.snapshot.LabelSetGetValue(ls.id, name)
+}
+
+// Has returns true if the label with the given name is present.
+func (ls Labels) Has(name string) bool {
+ if name == "" { // Avoid crash in loop if someone asks for "".
+ return false // Prometheus does not store blank label names.
+ }
+
+ if ls.IsZero() {
+ return false
+ }
+
+ if ls.dropMetricName && name == MetricName {
+ return false
+ }
+
+ return ls.snapshot.LabelSetHasLabelName(ls.id, name)
+}
+
+// HasDuplicateLabelNames returns whether ls has duplicate label names.
+// It assumes that the labelset is sorted.
+func (ls Labels) HasDuplicateLabelNames() (string, bool) {
+ if ls.IsZero() {
+ return "", false
+ }
+
+ return ls.snapshot.LabelSetHasDuplicateLabelNames(ls.id, ls.dropMetricName)
+}
+
+// Hash returns a hash value for the label set.
+// Note: the result is not guaranteed to be consistent across different runs of Prometheus.
+func (ls Labels) Hash() uint64 {
+ if ls.IsZero() {
+ return 0
+ }
+
+ return ls.snapshot.LabelSetHash(ls.id, ls.dropMetricName)
+}
+
+// HashForLabels returns a hash value for the labels matching the provided names.
+// 'names' have to be sorted in ascending order.
+func (ls Labels) HashForLabels(b []byte, names ...string) (uint64, []byte) {
+ if ls.IsZero() {
+ return 0, b[:0]
+ }
+
+ return ls.snapshot.LabelSetHashForLabels(ls.id, names, ls.dropMetricName), b
+}
+
+// HashWithoutLabels returns a hash value for all labels except those matching
+// the provided names. 'names' have to be sorted in ascending order.
+func (ls Labels) HashWithoutLabels(b []byte, names ...string) (uint64, []byte) {
+ if ls.IsZero() {
+ return 0, b[:0]
+ }
+
+ return ls.snapshot.LabelSetHashWithoutLabels(ls.id, names), b
+}
+
+// InternStrings calls intern on every string value inside ls, replacing them with what it returns.
+func (*Labels) InternStrings(func(string) string) {
+ // remove these calls as there is nothing to do.
+}
+
+// ID return id labelset.
+func (ls Labels) ID() uint32 {
+ return ls.id
+}
+
+// IsEmpty returns true if ls represents an empty set of labels.
+func (ls Labels) IsEmpty() bool {
+ return ls.Len() == 0
+}
+
+// IsZero returns true if ls lss referece is nil.
+// Implements yaml.IsZeroer - if we don't have this then 'omitempty' fields are always omitted.
+func (ls Labels) IsZero() bool {
+ if ls.length != 0 {
+ return false
+ }
+
+ if ls.snapshot == nil {
+ return true
+ }
+
+ ls.length = uint16(ls.snapshot.LabelSetLength(ls.id, ls.dropMetricName))
+ return ls.length == 0
+}
+
+// Len returns the number of labels.
+func (ls Labels) Len() int {
+ if ls.length != 0 {
+ return int(ls.length)
+ }
+
+ if ls.snapshot == nil {
+ return 0
+ }
+
+ ls.length = uint16(ls.snapshot.LabelSetLength(ls.id, ls.dropMetricName))
+ return int(ls.length)
+}
+
+// MatchLabels returns a subset of Labels that matches/does not match with the provided label names based on the
+// 'on' boolean. If on is set to true, it returns the subset of labels that match with the provided label names and
+// its inverse when 'on' is set to false.
+//
+//revive:disable-next-line:flag-parameter implementation
+func (ls Labels) MatchLabels(on bool, names ...string) Labels {
+ if ls.IsZero() {
+ return ls
+ }
+
+ nameSet := make(map[string]struct{}, len(names))
+ for _, n := range names {
+ nameSet[n] = struct{}{}
+ }
+
+ builder := NewScratchBuilder(ls.Len())
+ _ = ls.snapshot.RangeLabelSet(ls.id, ls.dropMetricName, func(l cppbridge.Label) error {
+ if _, ok := nameSet[l.Name]; on == ok && (on || l.Name != MetricName) {
+ builder.Add(l.Name, l.Value)
+ }
+
+ return nil
+ })
+
+ return builder.Labels()
+}
+
+// Range calls f on each label.
+func (ls Labels) Range(f func(l Label)) {
+ if ls.IsZero() {
+ return
+ }
+
+ _ = ls.snapshot.RangeLabelSet(ls.id, ls.dropMetricName, func(l cppbridge.Label) error {
+ f(Label(l))
+
+ return nil
+ })
+}
+
+// ReleaseStrings calls release on every string value inside ls.
+func (Labels) ReleaseStrings(_ func(string)) {
+ // remove these calls as there is nothing to do.
+}
+
+// RenewSnapshot renew ls snapshot.
+func (ls *Labels) RenewSnapshot() {
+ if ls.snapshot == nil {
+ return
+ }
+
+ // long way
+ if ls.snapshot.IsOutdated() {
+ b := &Builder{
+ base: *ls,
+ del: make([]string, 0, 1),
+ }
+ if b.base.dropMetricName {
+ b.del = append(b.del, MetricName)
+ }
+
+ *ls = b.rebuildLabels()
+ return
+ }
+
+ ls.snapshot = ls.snapshot.Snapshot()
+}
+
+// Validate calls f on each label. If f returns a non-nil error, then it returns that error canceling the iteration.
+func (ls Labels) Validate(f func(l Label) error) error {
+ if ls.IsZero() {
+ return nil
+ }
+
+ return ls.snapshot.RangeLabelSet(ls.id, ls.dropMetricName, func(l cppbridge.Label) error {
+ return f(Label(l))
+ })
+}
+
+// WithoutEmpty returns the labelset without empty labels.
+// May return the same labelset.
+func (ls Labels) WithoutEmpty() Labels {
+ return ls
+}
+
+//
+// Builder
+//
+
+// NewBuilderWithSymbolTable creates a Builder, for api parity with dedupelabels.
+func NewBuilderWithSymbolTable(*SymbolTable) *Builder {
+ return NewBuilder(EmptyLabels())
+}
+
+// Builder allows modifying Labels.
+type Builder struct {
+ base Labels
+ del []string
+ add []Label
+ skipCache bool
+}
+
+// Labels returns the labels from the builder.
+// If no modifications were made, the original labels are returned.
+func (b *Builder) Labels() Labels {
+ if len(b.del) == 0 && len(b.add) == 0 {
+ // quick exit if no changes
+ return b.base
+ }
+
+ return b.rebuildLabels()
+}
+
+// rebuildLabels returns labels from the builder built through labelsstorage.
+func (b *Builder) rebuildLabels() Labels {
+ slices.SortFunc(b.add, func(a, b Label) int { return strings.Compare(a.Name, b.Name) })
+ slices.Sort(b.del)
+
+ // dedup b.del
+ if len(b.del) > 1 {
+ for i := len(b.del) - 1; i != 0; i-- {
+ if b.del[i] == b.del[i-1] {
+ b.del = slices.Delete(b.del, i, i+1)
+ }
+ }
+ }
+
+ // clearing b.del(b.add has priority)
+ j := 0
+ for i := 0; i < len(b.add); i++ {
+ name := b.add[i].Name
+ for j < len(b.del) && b.del[j] < name {
+ j++
+ }
+
+ if j == len(b.del) {
+ break
+ }
+
+ if name == b.del[j] {
+ b.del = slices.Delete(b.del, j, j+1)
+ }
+ }
+
+ b.base = Storage.findOrEmplaceFromBuilder(b)
+ b.del = b.del[:0]
+ b.add = b.add[:0]
+
+ return b.base
+}
+
+// Reset clears all current state for the builder.
+func (b *Builder) Reset(base Labels) {
+ b.base = base
+ b.del = b.del[:0]
+ b.add = b.add[:0]
+ if b.base.dropMetricName {
+ b.del = append(b.del, MetricName)
+ }
+
+ b.base.Range(func(l Label) {
+ if l.Value == "" {
+ b.del = append(b.del, l.Name)
+ }
+ })
+}
+
+//
+// ScratchBuilder
+//
+
+// ScratchBuilder allows efficient construction of a Labels from scratch.
+type ScratchBuilder struct {
+ builder Builder
+ sorted bool
+}
+
+// NewScratchBuilder creates a ScratchBuilder initialized for Labels with n entries.
+func NewScratchBuilder(n int) ScratchBuilder {
+ return ScratchBuilder{
+ builder: Builder{add: make([]Label, 0, n)},
+ }
+}
+
+// NewScratchBuilderWithSymbolTable creates a ScratchBuilder, for api parity with dedupelabels.
+func NewScratchBuilderWithSymbolTable(_ *SymbolTable, n int) ScratchBuilder {
+ return NewScratchBuilder(n)
+}
+
+// Add a name/value pair.
+// Note if you Add the same name twice you will get a duplicate label, which is invalid.
+func (b *ScratchBuilder) Add(name, value string) {
+ if value == "" {
+ // Empty labels are the same as missing labels.
+ return
+ }
+
+ b.builder.add = append(b.builder.add, Label{Name: name, Value: value})
+ n := len(b.builder.add)
+ b.sorted = b.sorted && (n > 1 && b.builder.add[n-1].Name > b.builder.add[n-2].Name)
+}
+
+// Assign is for when you already have a Labels which you want this ScratchBuilder to return.
+func (b *ScratchBuilder) Assign(ls Labels) {
+ b.Reset()
+ b.builder.base.CopyFrom(ls)
+}
+
+// Labels returns the name/value pairs added as a Labels object. Calling Add() after Labels() has no effect.
+func (b *ScratchBuilder) Labels() Labels {
+ if len(b.builder.add) == 0 {
+ return b.builder.base
+ }
+
+ if !b.sorted {
+ b.Sort()
+ }
+
+ b.builder.base = Storage.findOrEmplaceFromBuilder(&b.builder)
+ b.builder.add = b.builder.add[:0]
+
+ // isvalid ?
+ return b.builder.base
+}
+
+// Overwrite write the newly-built Labels out to ls.
+func (b *ScratchBuilder) Overwrite(inls *Labels) {
+ inls.CopyFrom(b.Labels())
+}
+
+// Reset clear builder container.
+func (b *ScratchBuilder) Reset() {
+ b.builder.base = EmptyLabels()
+ b.builder.add = b.builder.add[:0]
+ b.sorted = false
+}
+
+// SetSkipCache set the flag to ignore caches.
+func (b *ScratchBuilder) SetSkipCache() {
+ b.builder.skipCache = true
+}
+
+// SetSymbolTable implementation.
+func (*ScratchBuilder) SetSymbolTable(*SymbolTable) {
+ // no-op
+}
+
+// Sort the labels added so far by name.
+func (b *ScratchBuilder) Sort() {
+ if b.sorted {
+ return
+ }
+
+ slices.SortFunc(b.builder.add, func(a, b Label) int { return strings.Compare(a.Name, b.Name) })
+ b.sorted = true
+}
+
+// UnsafeAddBytes add a name/value pair, using []byte instead of string.
+// The '-tags stringlabels' version of this function is unsafe, hence the name.
+// This version is safe - it copies the strings immediately - but we keep the same name so everything compiles.
+func (b *ScratchBuilder) UnsafeAddBytes(name, value []byte) {
+ b.Add(
+ unsafe.String(unsafe.SliceData(name), len(name)), // #nosec G103 // it's meant to be that way
+ unsafe.String(unsafe.SliceData(value), len(value)), // #nosec G103 // it's meant to be that way
+ )
+}
+
+//
+// SymbolTable
+//
+
+// SymbolTable is no-op, just for api parity with dedupelabels.
+type SymbolTable struct{}
+
+// NewSymbolTable init SymbolTable.
+func NewSymbolTable() *SymbolTable { return nil }
+
+// Len implementation.
+func (t *SymbolTable) Len() int { return 0 }
+
+//
+// help func
+//
+
+// Equal returns whether the two label sets are equal.
+func Equal(a, b Labels) bool {
+ aLen, bLen := a.Len(), b.Len()
+
+ if aLen == 0 && bLen == 0 {
+ return true
+ }
+
+ if aLen != bLen {
+ return false
+ }
+
+ return cppbridge.EqualLabelSets(
+ a.snapshot, b.snapshot,
+ a.id, b.id,
+ a.dropMetricName, b.dropMetricName,
+ )
+}
+
+// Compare compares the two label sets.
+// The result will be 0 if a==b, <0 if a < b, and >0 if a > b.
+func Compare(a, b Labels) int {
+ if a.Len() == 0 && b.Len() == 0 {
+ return 0
+ }
+
+ return cppbridge.CompareLabelSets(
+ a.snapshot, b.snapshot,
+ a.id, b.id,
+ a.dropMetricName, b.dropMetricName,
+ )
+}
+
+//
+// Storage
+//
+
+// Storage global label set storage.
+var Storage = newStorage()
+
+const (
+ metricsDuration = 30 * time.Second
+ rotateDuration = 5 * time.Minute
+)
+
+//
+// Adapter
+//
+
+// Adapter implementation for [storage.Adapter].
+type Adapter interface {
+ // FindFromBuilder label set from builder in lss, if not found return EmptyLabels.
+ FindFromBuilder(
+ builderSortedAdd []cppbridge.Label,
+ builderSortedDel []string,
+ builderSnapshot *cppbridge.LabelSetSnapshot,
+ hash uint64,
+ builderLSID uint32,
+ skipCache bool,
+ ) (Labels, bool)
+
+ // FindByHash label set by hash in cache.
+ FindByHash(
+ builderSortedAdd []cppbridge.Label,
+ builderSortedDel []string,
+ builderSnapshot *cppbridge.LabelSetSnapshot,
+ hash uint64,
+ builderLSID uint32,
+ ) (Labels, bool)
+}
+
+// noopAdapter implementation [Adapter] without operation.
+type noopAdapter struct{}
+
+// newNoopAdapter init new [*noopAdapter].
+func newNoopAdapter() *Adapter {
+ var nr Adapter = &noopAdapter{}
+ return &nr
+}
+
+// FindFromBuilder implementation [Adapter].
+func (*noopAdapter) FindFromBuilder(
+ _ []cppbridge.Label,
+ _ []string,
+ _ *cppbridge.LabelSetSnapshot,
+ _ uint64,
+ _ uint32,
+ _ bool,
+) (Labels, bool) {
+ return EmptyLabels(), false
+}
+
+// FindByHash implementation [Adapter].
+func (*noopAdapter) FindByHash(
+ _ []cppbridge.Label,
+ _ []string,
+ _ *cppbridge.LabelSetSnapshot,
+ _ uint64,
+ _ uint32,
+) (Labels, bool) {
+ return EmptyLabels(), false
+}
+
+//
+// storage
+//
+
+// storage for label set.
+type storage struct {
+ workingLSS *cppbridge.LSSWithSnapshot
+ adapter atomic.Pointer[Adapter]
+ writeLock sync.RWMutex
+ rotateLock sync.RWMutex
+ baseCtx context.Context
+ generation uint64
+ memoryInUse *prometheus.GaugeVec
+ lssSize *prometheus.GaugeVec
+ lssBitsetCount *prometheus.GaugeVec
+}
+
+// newStorage init new storage.
+func newStorage() *storage {
+ factory := util.NewUnconflictRegisterer(prometheus.DefaultRegisterer)
+
+ constLabels := prometheus.Labels{"allocator": "labels"}
+ s := &storage{
+ workingLSS: cppbridge.NewLSSWithSnapshot(cppbridge.NewLssStorage()),
+ writeLock: sync.RWMutex{},
+ rotateLock: sync.RWMutex{},
+ baseCtx: context.Background(),
+ memoryInUse: factory.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "prompp_labels_cgo_memory_bytes",
+ Help: "Current value of used memory in bytes.",
+ ConstLabels: constLabels,
+ },
+ []string{"generation"},
+ ),
+ lssSize: factory.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "prompp_labels_lss_size",
+ Help: "Current size of lss.",
+ ConstLabels: constLabels,
+ },
+ []string{"generation"},
+ ),
+ lssBitsetCount: factory.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "prompp_labels_lss_bitset_count",
+ Help: "Current count of emplace to lss bitset.",
+ ConstLabels: constLabels,
+ },
+ []string{"generation"},
+ ),
+ }
+ s.adapter.Store(newNoopAdapter())
+
+ return s
+}
+
+// SetAdapter store [Adapter].
+func (s *storage) SetAdapter(adapter Adapter) {
+ s.adapter.Store(&adapter)
+}
+
+// Run starts goroutine of the metric collector and the cleaner.
+func (s *storage) Run(ctx context.Context) {
+ go s.observeAndClean(ctx)
+}
+
+// FindOrEmplaceLabelSet find ls from bulder in current lsses or store to working LSS and return Labels.
+func (s *storage) findOrEmplaceFromBuilder(b *Builder) Labels {
+ sortedAdd := *((*[]cppbridge.Label)(unsafe.Pointer(&b.add)))
+ hash, empty := cppbridge.LabelSetFromBuilderHash(sortedAdd, b.del, b.base.snapshot, b.base.id)
+ if empty {
+ return EmptyLabels()
+ }
+
+ adapter := *s.adapter.Load()
+ if ls, find := adapter.FindByHash(sortedAdd, b.del, b.base.snapshot, hash, b.base.id); find {
+ return ls
+ }
+
+ if ls, find := s.findFromBuilder(sortedAdd, b.del, b.base.snapshot, hash, b.base.id); find {
+ return ls
+ }
+
+ if ls, find := adapter.FindFromBuilder(
+ sortedAdd,
+ b.del,
+ b.base.snapshot,
+ hash,
+ b.base.id,
+ b.skipCache,
+ ); find {
+ return ls
+ }
+
+ s.rotateLock.RLock()
+
+ s.writeLock.Lock()
+ lsID, length := s.workingLSS.FindOrEmplaceFromBuilder(
+ sortedAdd,
+ b.del,
+ b.base.snapshot,
+ hash,
+ b.base.id,
+ )
+ s.writeLock.Unlock()
+
+ snapshot := s.workingLSS.Snapshot()
+
+ s.rotateLock.RUnlock()
+
+ return NewLabelsWithLSS(snapshot, nil, lsID, length)
+}
+
+// findFromBuilder find labelset from builder in lss, return length ls, lsid and bool ok.
+func (s *storage) findFromBuilder(
+ builderSortedAdd []cppbridge.Label,
+ builderSortedDel []string,
+ builderSnapshot *cppbridge.LabelSetSnapshot,
+ hash uint64,
+ builderLSID uint32,
+) (Labels, bool) {
+ s.rotateLock.RLock()
+ s.writeLock.RLock()
+
+ lsID, length, find := s.workingLSS.FindFromBuilder(
+ builderSortedAdd,
+ builderSortedDel,
+ builderSnapshot,
+ hash,
+ builderLSID,
+ )
+ if !find {
+ s.writeLock.RUnlock()
+ s.rotateLock.RUnlock()
+ return EmptyLabels(), false
+ }
+
+ s.writeLock.RUnlock()
+
+ snapshot := s.workingLSS.Snapshot()
+
+ s.rotateLock.RUnlock()
+
+ return NewLabelsWithLSS(
+ snapshot,
+ nil,
+ lsID,
+ length,
+ ), true
+}
+
+// observeAndClean write metrics for lss and rotate if necessary.
+func (s *storage) observeAndClean(ctx context.Context) {
+ metricsTimer := time.NewTimer(metricsDuration)
+ rotateTimer := time.NewTimer(rotateDuration)
+
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case <-metricsTimer.C:
+ s.writeLock.Lock()
+ am, lssSize, lssBitsetCount := s.workingLSS.Stats()
+ s.writeLock.Unlock()
+
+ ls := prometheus.Labels{"generation": strconv.FormatUint(s.generation, 10)}
+ s.memoryInUse.With(ls).Set(float64(am))
+ s.lssSize.With(ls).Set(float64(lssSize))
+ s.lssBitsetCount.With(ls).Set(float64(lssBitsetCount))
+
+ metricsTimer.Reset(metricsDuration)
+
+ case <-rotateTimer.C:
+ s.writeLock.Lock()
+ lssSize, lssBitsetCount := s.workingLSS.StatsWithReset()
+ s.writeLock.Unlock()
+
+ if uint64(lssBitsetCount) <= lssSize/2 {
+ ls := prometheus.Labels{"generation": strconv.FormatUint(s.generation, 10)}
+ s.memoryInUse.Delete(ls)
+ s.lssSize.Delete(ls)
+ s.lssBitsetCount.Delete(ls)
+
+ s.rotateLock.Lock()
+ s.workingLSS.Outdate()
+ s.workingLSS = cppbridge.NewLSSWithSnapshot(cppbridge.NewLssStorage())
+ s.generation++
+ s.rotateLock.Unlock()
+
+ metricsTimer.Reset(0)
+ }
+
+ rotateTimer.Reset(rotateDuration)
+ }
+ }
+}
diff --git a/model/labels/labels_cpp_help_func_test.go b/model/labels/labels_cpp_help_func_test.go
new file mode 100644
index 0000000000..6cb8fc014c
--- /dev/null
+++ b/model/labels/labels_cpp_help_func_test.go
@@ -0,0 +1,808 @@
+//go:build cpplabels
+
+package labels_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+
+ "github.com/prometheus/prometheus/model/labels"
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
+ "github.com/prometheus/prometheus/pp/go/model"
+)
+
+//
+// help func
+//
+
+type HelpFuncSuite struct {
+ suite.Suite
+}
+
+func TestHelpFuncSuite(t *testing.T) {
+ suite.Run(t, new(HelpFuncSuite))
+}
+
+func (s *HelpFuncSuite) TestEqual() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.True(labels.Equal(lsA, lsB))
+}
+
+func (s *HelpFuncSuite) TestEqualDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.True(labels.Equal(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestEqualDropMetricName_2() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.True(labels.Equal(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestEqualEmpty() {
+ lsA := labels.EmptyLabels()
+
+ lsB := labels.EmptyLabels()
+
+ s.True(labels.Equal(lsA, lsB))
+}
+
+func (s *HelpFuncSuite) TestEqualEmptyDropMetricName() {
+ lsA := labels.FromStrings("__name__", "ubername")
+
+ lsB := labels.EmptyLabels()
+
+ s.True(labels.Equal(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestEqualOneLSS() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsid := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsid, 0)
+
+ s.True(labels.Equal(lsA, lsB))
+ s.True(labels.Equal(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestEqualOneLSSDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsid := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsid, 0)
+
+ s.True(labels.Equal(lsA.DropMetricName(), lsB))
+ s.True(labels.Equal(lsB, lsA.DropMetricName()))
+}
+
+func (s *HelpFuncSuite) TestEqualTwoLSS() {
+ lssA := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsInA := model.LabelSetFromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+ lsidA := lssA.FindOrEmplace(lsInA)
+ lsA := labels.NewLabelsWithLSS(lssA.Snapshot(), nil, lsidA, uint16(lsInA.Len()))
+
+ lssB := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsInB := model.LabelSetFromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+ lsidB := lssB.FindOrEmplace(lsInB)
+ lsB := labels.NewLabelsWithLSS(lssB.Snapshot(), nil, lsidB, uint16(lsInB.Len()))
+
+ s.True(labels.Equal(lsA, lsB))
+ s.True(labels.Equal(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestNotEqualOnLen() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "imya": "reck",
+ })
+
+ s.False(labels.Equal(lsA, lsB))
+}
+
+func (s *HelpFuncSuite) TestNotEqualOnLenDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "imya": "reck",
+ })
+
+ s.False(labels.Equal(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestNotEqualOnLenAnyLSS() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "imya": "reck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.False(labels.Equal(lsA, lsB))
+}
+
+func (s *HelpFuncSuite) TestNotEqualOnLenAnyLSSDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "imya": "reck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.False(labels.Equal(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestNotEqualOnLabel() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "imya": "reck",
+ })
+
+ s.False(labels.Equal(lsA, lsB))
+}
+
+func (s *HelpFuncSuite) TestNotEqualOnLabelDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "imya": "reck",
+ })
+
+ s.False(labels.Equal(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestNotEqualOnLabelAnyLSS() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "imya": "reck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.False(labels.Equal(lsA, lsB))
+}
+
+func (s *HelpFuncSuite) TestNotEqualOnLabelAnyLSSDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "lol": "kek",
+ "imya": "reck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.False(labels.Equal(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestNotEqualOnEmpty() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.EmptyLabels()
+
+ s.False(labels.Equal(lsA, lsB))
+}
+
+func (s *HelpFuncSuite) TestCompare() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(0, labels.Compare(lsA, lsB))
+}
+
+func (s *HelpFuncSuite) TestCompareDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(0, labels.Compare(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestCompareDropMetricName_2() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(0, labels.Compare(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestCompareEmpty() {
+ lsA := labels.EmptyLabels()
+
+ lsB := labels.EmptyLabels()
+
+ s.Equal(0, labels.Compare(lsA, lsB))
+}
+
+func (s *HelpFuncSuite) TestCompareEmptyDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ })
+
+ lsB := labels.EmptyLabels()
+
+ s.Equal(0, labels.Compare(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestCompareAnyLSS() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.Equal(0, labels.Compare(lsA, lsB))
+}
+
+func (s *HelpFuncSuite) TestCompareAnyLSSDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.Equal(0, labels.Compare(lsA.DropMetricName(), lsB))
+}
+
+func (s *HelpFuncSuite) TestCompareLength() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "ximya": "reck",
+ "zimya": "reck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareLengthDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ "ximya": "reck",
+ "zimya": "reck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(1, labels.Compare(lsA, lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareNameLength() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lolk": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareNameLengthDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lolk": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(1, labels.Compare(lsA, lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareAnyLSSNameLength() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lolk": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareAnyLSSNameLengthDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lolk": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.Equal(1, labels.Compare(lsA, lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareNameString() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lok": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareNameStringDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lok": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(1, labels.Compare(lsA, lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareALSSNameString() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lok": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 3)
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareALSSNameStringDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lok": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 3)
+
+ s.Equal(1, labels.Compare(lsA, lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareValueLength() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kkek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareValueLengthDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kkek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ s.Equal(1, labels.Compare(lsA, lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareLSSValueLength() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kkek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareLSSValueLengthDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kkek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.Equal(1, labels.Compare(lsA, lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareValueString() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kak",
+ "che": "bureck",
+ })
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareValueStringDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kak",
+ "che": "bureck",
+ })
+
+ s.Equal(1, labels.Compare(lsA, lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareLSSValueString() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kak",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareLSSValueStringDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kak",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.Equal(1, labels.Compare(lsA, lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareOnLabelAnyLSS() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "imya": "reck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareOnLabelAnyLSSDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "imya": "reck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsidB := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsB := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsidB, 0)
+
+ s.Equal(1, labels.Compare(lsA, lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareOnEmpty() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.EmptyLabels()
+
+ s.Equal(1, labels.Compare(lsA, lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA))
+}
+
+func (s *HelpFuncSuite) TestCompareOnEmptyDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.EmptyLabels()
+
+ s.Equal(1, labels.Compare(lsA.DropMetricName(), lsB))
+ s.Equal(-1, labels.Compare(lsB, lsA.DropMetricName()))
+}
+
+func (s *HelpFuncSuite) TestCompareOnEmptyDropMetricName_2() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ })
+
+ s.Equal(1, labels.Compare(lsA.DropMetricName(), lsB.DropMetricName()))
+ s.Equal(-1, labels.Compare(lsB.DropMetricName(), lsA.DropMetricName()))
+}
+
+func BenchmarkLabels_Equal(b *testing.B) {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ for i := 0; i < b.N; i++ {
+ _ = labels.Equal(lsA, lsB.DropMetricName())
+ }
+}
diff --git a/model/labels/labels_cpp_scratch_builder_test.go b/model/labels/labels_cpp_scratch_builder_test.go
new file mode 100644
index 0000000000..ceb2b3ddc6
--- /dev/null
+++ b/model/labels/labels_cpp_scratch_builder_test.go
@@ -0,0 +1,107 @@
+//go:build cpplabels
+
+package labels_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+
+ "github.com/prometheus/prometheus/model/labels"
+)
+
+//
+// ScratchBuilder
+//
+
+type ScratchBuilderSuite struct {
+ suite.Suite
+}
+
+func TestScratchBuilderSuite(t *testing.T) {
+ suite.Run(t, new(ScratchBuilderSuite))
+}
+
+func (s *ScratchBuilderSuite) TestAdd() {
+ lsA := labels.FromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ builder := labels.NewScratchBuilder(lsA.Len())
+ lsA.Range(func(l labels.Label) {
+ builder.Add(l.Name, l.Value)
+ })
+
+ s.True(labels.Equal(lsA, builder.Labels()))
+}
+
+func (s *ScratchBuilderSuite) TestAssign() {
+ lsA := labels.FromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ builder := labels.NewScratchBuilder(lsA.Len())
+ builder.Assign(lsA)
+
+ s.True(labels.Equal(lsA, builder.Labels()))
+}
+
+func (s *ScratchBuilderSuite) TestOverwrite() {
+ lsA := labels.FromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+ builder := labels.NewScratchBuilder(lsA.Len())
+ builder.Assign(lsA)
+
+ lsB := labels.EmptyLabels()
+
+ builder.Overwrite(&lsB)
+
+ s.True(labels.Equal(lsA, builder.Labels()))
+ s.True(labels.Equal(lsA, lsB))
+}
+
+func (s *ScratchBuilderSuite) TestReset() {
+ lsA := labels.FromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+ builder := labels.NewScratchBuilder(lsA.Len())
+ builder.Assign(lsA)
+ builder.Sort()
+ builder.Reset()
+
+ s.False(labels.Equal(lsA, builder.Labels()))
+}
+
+func (s *ScratchBuilderSuite) TestUnsafeAddBytes() {
+ lsA := labels.FromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ builder := labels.NewScratchBuilder(lsA.Len())
+ lsA.Range(func(l labels.Label) {
+ builder.UnsafeAddBytes([]byte(l.Name), []byte(l.Value))
+ })
+
+ s.True(labels.Equal(lsA, builder.Labels()))
+}
diff --git a/model/labels/labels_cpp_test.go b/model/labels/labels_cpp_test.go
new file mode 100644
index 0000000000..b19cf9abac
--- /dev/null
+++ b/model/labels/labels_cpp_test.go
@@ -0,0 +1,1297 @@
+//go:build cpplabels
+
+package labels_test
+
+import (
+ "errors"
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+
+ "github.com/prometheus/prometheus/model/labels"
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
+ "github.com/prometheus/prometheus/pp/go/model"
+)
+
+type LabelsSuite struct {
+ suite.Suite
+}
+
+func TestLabelsSuite(t *testing.T) {
+ suite.Run(t, new(LabelsSuite))
+}
+
+func (s *LabelsSuite) TestBytes() {
+ expected := []byte{
+ 254, 95, 95, 110, 97, 109, 101, 95, 95, 255, 117, 98,
+ 101, 114, 110, 97, 109, 101, 255, 99, 104, 101, 255, 98,
+ 117, 114, 101, 99, 107, 255, 108, 111, 108, 255, 107, 101,
+ 107, 255, 122, 105, 109, 121, 97, 255, 114, 101, 99, 107,
+ }
+
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(expected, lsA.Bytes(nil))
+}
+
+func (s *LabelsSuite) TestBytesDropMetricName() {
+ expected := []byte{
+ 254, 99, 104, 101, 255, 98, 117, 114, 101, 99,
+ 107, 255, 108, 111, 108, 255, 107, 101, 107, 255,
+ 122, 105, 109, 121, 97, 255, 114, 101, 99, 107,
+ }
+
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(expected, lsA.DropMetricName().Bytes(nil))
+}
+
+func (s *LabelsSuite) TestBytesWithLabels() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.Bytes(nil), lsB.BytesWithLabels(nil, "__name__", "lol"))
+}
+
+func (s *LabelsSuite) TestBytesWithLabelsDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.Bytes(nil), lsB.DropMetricName().BytesWithLabels(nil, "__name__", "lol"))
+}
+
+func (s *LabelsSuite) TestBytesWithLabelsEmpty() {
+ lsA := labels.FromStrings()
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.Bytes(nil), lsB.BytesWithLabels(nil))
+}
+
+func (s *LabelsSuite) TestBytesWithLabelsEmptyDropMetricName() {
+ lsA := labels.FromStrings()
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.Bytes(nil), lsB.DropMetricName().BytesWithLabels(nil, "__name__"))
+}
+
+func (s *LabelsSuite) TestBytesWithoutLabels() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.Bytes(nil), lsB.BytesWithoutLabels(nil, "che", "zimya"))
+}
+
+func (s *LabelsSuite) TestBytesWithoutLabelsDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.Bytes(nil), lsB.DropMetricName().BytesWithoutLabels(nil, "che", "zimya"))
+}
+
+func (s *LabelsSuite) TestBytesWithoutLabels_2() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.Bytes(nil), lsB.BytesWithoutLabels(nil, "__name__", "che", "zimya"))
+}
+
+func (s *LabelsSuite) TestBytesWithoutLabelsEmpty() {
+ lsA := labels.FromStrings()
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.Bytes(nil), lsB.BytesWithoutLabels(nil, "__name__", "che", "lol", "zimya"))
+}
+
+func (s *LabelsSuite) TestBytesWithoutLabelsEmptyDropMetricName() {
+ lsA := labels.FromStrings()
+
+ lsB := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.Bytes(nil), lsB.DropMetricName().BytesWithoutLabels(nil, "che", "lol", "zimya"))
+}
+
+func (s *LabelsSuite) TestBytesWithoutLabelsEmptyNames() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.Bytes(nil), lsA.BytesWithoutLabels(nil))
+}
+
+func (s *LabelsSuite) TestBytesWithoutLabelsEmptyNamesDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsA.DropMetricName().Bytes(nil), lsA.DropMetricName().BytesWithoutLabels(nil))
+}
+
+func (s *LabelsSuite) TestCopy() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ lsB := lsA.Copy()
+
+ s.Equal(lsA.Bytes(nil), lsB.Bytes(nil))
+ s.True(labels.Equal(lsA, lsB))
+}
+
+func (s *LabelsSuite) TestCopyDropMetricName() {
+ lsExpected := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ lsB := lsA.Copy()
+
+ s.Equal(lsExpected.Bytes(nil), lsB.DropMetricName().Bytes(nil))
+ s.NotEqual(lsA.Bytes(nil), lsB.DropMetricName().Bytes(nil))
+ s.True(labels.Equal(lsExpected, lsB.DropMetricName()))
+ s.False(labels.Equal(lsA, lsB.DropMetricName()))
+}
+
+func (s *LabelsSuite) TestCopyEmpty() {
+ lsA := labels.FromStrings()
+
+ lsB := lsA.Copy()
+
+ s.Equal(lsA.Bytes(nil), lsB.Bytes(nil))
+ s.True(labels.Equal(lsA, lsB))
+}
+
+func (s *LabelsSuite) TestCopyEmptyDropMetricName() {
+ lsExpected := labels.FromStrings()
+
+ lsA := labels.FromStrings("__name__", "ubername")
+
+ lsB := lsA.Copy()
+
+ s.Equal(lsExpected.Bytes(nil), lsB.DropMetricName().Bytes(nil))
+ s.NotEqual(lsA.Bytes(nil), lsB.DropMetricName().Bytes(nil))
+ s.True(labels.Equal(lsExpected, lsB.DropMetricName()))
+ s.False(lsA.IsEmpty())
+}
+
+func (s *LabelsSuite) TestCopyFrom() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ lsB := labels.FromStrings()
+ lsB.CopyFrom(lsA)
+
+ s.Equal(lsA.Bytes(nil), lsB.Bytes(nil))
+ s.True(labels.Equal(lsA, lsB))
+}
+
+func (s *LabelsSuite) TestCopyFromDropMetricName() {
+ lsExpected := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ lsB := labels.FromStrings()
+ lsB.CopyFrom(lsA.DropMetricName())
+
+ s.Equal(lsExpected.Bytes(nil), lsB.Bytes(nil))
+ s.NotEqual(lsA.Bytes(nil), lsB.Bytes(nil))
+ s.True(labels.Equal(lsExpected, lsB))
+ s.False(labels.Equal(lsA, lsB))
+}
+
+func (s *LabelsSuite) TestCopyFromEmpty() {
+ lsA := labels.FromStrings()
+
+ lsB := labels.FromStrings()
+ lsB.CopyFrom(lsA)
+
+ s.Equal(lsA.Bytes(nil), lsB.Bytes(nil))
+ s.True(labels.Equal(lsA, lsB))
+}
+
+func (s *LabelsSuite) TestCopyFromEmptyDropMetricName() {
+ lsExpected := labels.FromStrings()
+
+ lsA := labels.FromStrings("__name__", "ubername")
+
+ lsB := labels.FromStrings()
+ lsB.CopyFrom(lsA.DropMetricName())
+
+ s.Equal(lsExpected.Bytes(nil), lsB.Bytes(nil))
+ s.NotEqual(lsA.Bytes(nil), lsB.Bytes(nil))
+ s.True(labels.Equal(lsExpected, lsB))
+ s.False(labels.Equal(lsA, lsB))
+}
+
+func (s *LabelsSuite) TestDropMetricName() {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsB.Bytes(nil), lsA.DropMetricName().Bytes(nil))
+ s.True(labels.Equal(lsA.DropMetricName(), lsB))
+}
+
+func (s *LabelsSuite) TestDropMetricName_2() {
+ lsA := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsB.Bytes(nil), lsA.DropMetricName().Bytes(nil))
+ s.True(labels.Equal(lsA.DropMetricName(), lsB))
+}
+
+func (s *LabelsSuite) TestDropMetricName_3() {
+ original := labels.FromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ lsB := labels.FromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ lsC := labels.FromMap(map[string]string{
+ "__aaa__": "11111",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ s.Equal(lsC.Bytes(nil), lsB.DropMetricName().Bytes(nil))
+ s.True(labels.Equal(lsB.DropMetricName(), lsC))
+
+ s.Equal(original.Bytes(nil), lsB.Bytes(nil))
+ s.True(labels.Equal(original, lsB))
+}
+
+func (s *LabelsSuite) TestGetDropMetricName() {
+ lsMap := map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ }
+
+ lsA := labels.FromMap(lsMap)
+
+ for ln, lv := range lsMap {
+ s.Equal(lv, lsA.Get(ln))
+ }
+
+ s.Equal("", lsA.DropMetricName().Get("__name__"))
+}
+
+func (s *LabelsSuite) TestHasDropMetricName() {
+ lsMap := map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ }
+
+ lsA := labels.FromMap(lsMap)
+
+ for ln := range lsMap {
+ s.True(lsA.Has(ln))
+ }
+
+ s.False(lsA.DropMetricName().Has("__name__"))
+}
+
+func (s *LabelsSuite) TestHasDuplicateLabelNamesFalseDropMetricName() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+ _, has := lsA.DropMetricName().HasDuplicateLabelNames()
+
+ s.False(has)
+}
+
+func (s *LabelsSuite) TestHasDuplicateLabelNamesFalseDropMetricName_2() {
+ expected := `{__aaa__="11111", che="bureck", lol="kek", zimya="reck"}`
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "__name__", "ubername2",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+ name, has := lsA.DropMetricName().HasDuplicateLabelNames()
+
+ s.Equal(expected, lsA.DropMetricName().String())
+ s.False(has)
+ s.Equal("", name)
+}
+
+func (s *LabelsSuite) TestHasDuplicateLabelNamesTrueDropMetricName() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+ name, has := lsA.DropMetricName().HasDuplicateLabelNames()
+
+ s.True(has)
+ s.Equal("lol", name)
+}
+
+func (s *LabelsSuite) TestHashDropMetricName() {
+ lsA := labels.FromStrings(
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ s.Equal(lsA.DropMetricName().Hash(), lsB.Hash())
+}
+
+func (s *LabelsSuite) TestHashDropMetricName_2() {
+ lsA := labels.FromStrings(
+ "__name__", "ubername",
+ "__name__", "ubername2",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ s.Equal(lsA.DropMetricName().Hash(), lsB.Hash())
+}
+
+func (s *LabelsSuite) TestHashDropMetricName_3() {
+ lsA := labels.FromStrings(
+ "__name__", "ubername",
+ )
+
+ lsB := labels.FromStrings()
+
+ s.Equal(lsA.DropMetricName().Hash(), lsB.Hash())
+}
+
+func (s *LabelsSuite) TestHashForLabelsDropMetricName() {
+ lsA := labels.FromStrings(
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ hash1 := lsA.DropMetricName().Hash()
+ hash2, _ := lsB.HashForLabels(nil, "che", "lol", "zimya")
+
+ s.Equal(hash1, hash2)
+}
+
+func (s *LabelsSuite) TestHashForLabelsDropMetricName_2() {
+ lsA := labels.FromStrings(
+ "__name__", "ubername",
+ "__name__", "ubername2",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "__name__", "ubername2",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ hash1 := lsA.DropMetricName().Hash()
+ hash2, _ := lsB.HashForLabels(nil, "che", "lol", "zimya")
+
+ s.Equal(hash1, hash2)
+}
+
+func (s *LabelsSuite) TestHashForLabelsDropMetricName_3() {
+ lsA := labels.FromStrings(
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ hash1 := lsA.DropMetricName().Hash()
+ hash2, _ := lsB.DropMetricName().HashForLabels(nil, "__name__", "che", "lol", "zimya")
+
+ s.Equal(hash1, hash2)
+}
+
+func (s *LabelsSuite) TestHashWithoutLabelsDropMetricName() {
+ lsA := labels.FromStrings(
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ hash1 := lsA.DropMetricName().Hash()
+ hash2, _ := lsB.HashWithoutLabels(nil, "__aaa__")
+
+ s.Equal(hash1, hash2)
+}
+
+func (s *LabelsSuite) TestHashWithoutLabelsDropMetricName_2() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ hash1 := lsA.DropMetricName().Hash()
+ hash2, _ := lsB.HashWithoutLabels(nil)
+
+ s.Equal(hash1, hash2)
+}
+
+func (s *LabelsSuite) TestIsEmptyTrueDropMetricName() {
+ lsA := labels.FromStrings("__name__", "ubername")
+
+ s.False(lsA.IsEmpty())
+ lsDMN := lsA.DropMetricName()
+ s.True(lsDMN.IsEmpty())
+}
+
+func (s *LabelsSuite) TestLenDropMetricName() {
+ lsIn := model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsid := lss.FindOrEmplace(lsIn)
+ ls := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsid, 0)
+
+ lsDMN := ls.DropMetricName()
+ s.Equal(lsIn.Len()-1, lsDMN.Len())
+}
+
+func (s *LabelsSuite) TestLenDropMetricName_2() {
+ lsIn := model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsid := lss.FindOrEmplace(lsIn)
+ ls := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsid, uint16(lsIn.Len()))
+
+ lsDMN := ls.DropMetricName()
+ s.Equal(lsIn.Len()-1, lsDMN.Len())
+}
+
+func (s *LabelsSuite) TestLenDropMetricName_3() {
+ lsIn := model.LabelSetFromMap(map[string]string{
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsid := lss.FindOrEmplace(lsIn)
+ ls := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsid, uint16(lsIn.Len()))
+
+ lsDMN := ls.DropMetricName()
+ s.Equal(lsIn.Len(), lsDMN.Len())
+}
+
+func (s *LabelsSuite) TestLenEmptyDropMetricName() {
+ lsA := labels.EmptyLabels()
+
+ lsDMN := lsA.DropMetricName()
+ s.Equal(0, lsDMN.Len())
+}
+
+func (s *LabelsSuite) TestMatchLabelsTrueDropMetricName() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "lol", "kek",
+ "che", "bureck",
+ )
+
+ lsC := labels.FromStrings(
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ s.Equal(lsA.DropMetricName().MatchLabels(true, "__name__", "che", "lol").String(), lsB.String())
+ s.Equal(lsA.DropMetricName().MatchLabels(true, "che", "lol", "zimya").String(), lsC.String())
+
+ s.True(labels.Equal(lsA.DropMetricName().MatchLabels(true, "__name__", "che", "lol"), lsB))
+ s.True(labels.Equal(lsA.DropMetricName().MatchLabels(true, "che", "lol", "zimya"), lsC))
+}
+
+func (s *LabelsSuite) TestMatchLabelsTrueDropMetricName_2() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "__name__", "ubername2",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "lol", "kek",
+ "che", "bureck",
+ )
+
+ lsC := labels.FromStrings(
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ s.Equal(lsA.DropMetricName().MatchLabels(true, "__name__", "che", "lol").String(), lsB.String())
+ s.Equal(lsA.DropMetricName().MatchLabels(true, "che", "lol", "zimya").String(), lsC.String())
+
+ s.True(labels.Equal(lsA.DropMetricName().MatchLabels(true, "__name__", "che", "lol"), lsB))
+ s.True(labels.Equal(lsA.DropMetricName().MatchLabels(true, "che", "lol", "zimya"), lsC))
+}
+
+func (s *LabelsSuite) TestMatchLabelsFalseDropMetricName() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "zimya", "reck",
+ )
+
+ lsC := labels.FromStrings(
+ "__aaa__", "11111",
+ )
+
+ s.Equal(lsA.DropMetricName().MatchLabels(false, "__name__", "che", "lol").String(), lsB.String())
+ s.Equal(lsA.DropMetricName().MatchLabels(false, "che", "lol", "zimya").String(), lsC.String())
+
+ s.True(labels.Equal(lsA.DropMetricName().MatchLabels(false, "__name__", "che", "lol"), lsB))
+ s.True(labels.Equal(lsA.DropMetricName().MatchLabels(false, "che", "lol", "zimya"), lsC))
+}
+
+func (s *LabelsSuite) TestMatchLabelsFalseDropMetricName_2() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "__name__", "ubername2",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "zimya", "reck",
+ )
+
+ lsC := labels.FromStrings(
+ "__aaa__", "11111",
+ )
+
+ s.Equal(lsA.DropMetricName().MatchLabels(false, "__name__", "che", "lol").String(), lsB.String())
+ s.Equal(lsA.DropMetricName().MatchLabels(false, "che", "lol", "zimya").String(), lsC.String())
+
+ s.True(labels.Equal(lsA.DropMetricName().MatchLabels(false, "__name__", "che", "lol"), lsB))
+ s.True(labels.Equal(lsA.DropMetricName().MatchLabels(false, "che", "lol", "zimya"), lsC))
+}
+
+func (s *LabelsSuite) TestRangeDropMetricName() {
+ lsMapA := map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ }
+ lsA := labels.FromMap(lsMapA)
+
+ lsMapB := make(map[string]string, len(lsMapA))
+ lsA.DropMetricName().Range(func(l labels.Label) {
+ lsMapB[l.Name] = l.Value
+ })
+
+ delete(lsMapA, "__name__")
+
+ s.Equal(lsMapA, lsMapB)
+}
+
+func (s *LabelsSuite) TestValidateDropMetricName() {
+ lsMapA := map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ }
+ lsA := labels.FromMap(lsMapA)
+
+ errEnd := errors.New("end")
+ delete(lsMapA, "__name__")
+ length := len(lsMapA)
+
+ lsMapB := make(map[string]string, length)
+ err := lsA.DropMetricName().Validate(func(l labels.Label) error {
+ lsMapB[l.Name] = l.Value
+
+ length--
+ if length == 0 {
+ return errEnd
+ }
+ return nil
+ })
+
+ s.Equal(lsMapA, lsMapB)
+ s.ErrorIs(err, errEnd)
+}
+
+func (s *LabelsSuite) TestWithoutEmptyDropMetricName() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ s.Equal(lsA.Bytes(nil), lsB.DropMetricName().WithoutEmpty().Bytes(nil))
+ s.True(labels.Equal(lsA, lsB.DropMetricName().WithoutEmpty()))
+}
+
+func (s *LabelsSuite) TestIsZeroDropMetricName() {
+ lsA := labels.FromStrings(
+ "__name__", "ubername",
+ )
+
+ lsDMN := lsA.DropMetricName()
+ s.True(lsDMN.IsZero())
+}
+
+func (s *LabelsSuite) TestGet() {
+ lsMap := map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ }
+
+ lsA := labels.FromMap(lsMap)
+
+ for ln, lv := range lsMap {
+ s.Equal(lv, lsA.Get(ln))
+ }
+
+ s.Equal("", lsA.Get("boo"))
+}
+
+func (s *LabelsSuite) TestHas() {
+ lsMap := map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ }
+
+ lsA := labels.FromMap(lsMap)
+
+ for ln := range lsMap {
+ s.True(lsA.Has(ln))
+ }
+
+ s.False(lsA.Has("boo"))
+}
+
+func (s *LabelsSuite) TestHasDuplicateLabelNamesFalse() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+ _, has := lsA.HasDuplicateLabelNames()
+
+ s.False(has)
+}
+
+func (s *LabelsSuite) TestHasDuplicateLabelNamesTrue() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+ name, has := lsA.HasDuplicateLabelNames()
+
+ s.True(has)
+ s.Equal("lol", name)
+}
+
+func (s *LabelsSuite) TestHash() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ hash1, hash2, hash3 := lsA.Hash(), lsA.Hash(), lsB.Hash()
+
+ s.Equal(hash1, hash2)
+ s.NotEqual(hash3, hash1)
+}
+
+func (s *LabelsSuite) TestHashForLabels() {
+ lsA := labels.FromStrings(
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ hash1 := lsA.Hash()
+ hash2, _ := lsB.HashForLabels(nil, "__name__", "che", "lol", "zimya")
+
+ s.Equal(hash1, hash2)
+}
+
+func (s *LabelsSuite) TestHashWithoutLabels() {
+ lsA := labels.FromStrings(
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ hash1 := lsA.Hash()
+ hash2, _ := lsB.HashWithoutLabels(nil, "__aaa__")
+
+ s.Equal(hash1, hash2)
+}
+
+func (s *LabelsSuite) TestHashWithoutLabels_2() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ hash1 := lsA.Hash()
+ hash2, _ := lsB.HashWithoutLabels(nil)
+
+ s.Equal(hash1, hash2)
+}
+
+func (s *LabelsSuite) TestIsEmptyTrue() {
+ lsA := labels.FromStrings()
+ lsB := labels.EmptyLabels()
+
+ s.True(lsA.IsEmpty())
+ s.True(lsB.IsEmpty())
+}
+
+func (s *LabelsSuite) TestIsEmptyFalse() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ s.False(lsA.IsEmpty())
+}
+
+func (s *LabelsSuite) TestLen() {
+ lsIn := model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsid := lss.FindOrEmplace(lsIn)
+ ls := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsid, 0)
+
+ s.Equal(lsIn.Len(), ls.Len())
+}
+
+func (s *LabelsSuite) TestLen_2() {
+ lsIn := model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ })
+
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsid := lss.FindOrEmplace(lsIn)
+ ls := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsid, uint16(lsIn.Len()))
+
+ s.Equal(lsIn.Len(), ls.Len())
+}
+
+func (s *LabelsSuite) TestLenEmpty() {
+ lsA := labels.FromStrings()
+ lsB := labels.EmptyLabels()
+
+ s.Equal(0, lsA.Len())
+ s.Equal(0, lsB.Len())
+}
+
+func (s *LabelsSuite) TestMatchLabelsTrue() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ )
+
+ lsC := labels.FromStrings(
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ s.True(labels.Equal(lsA.MatchLabels(true, "__name__", "che", "lol"), lsB))
+ s.True(labels.Equal(lsA.MatchLabels(true, "che", "lol", "zimya"), lsC))
+}
+
+func (s *LabelsSuite) TestMatchLabelsFalse() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "kek",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "zimya", "reck",
+ )
+
+ lsC := labels.FromStrings(
+ "__aaa__", "11111",
+ )
+
+ s.True(labels.Equal(lsA.MatchLabels(false, "__name__", "che", "lol"), lsB))
+ s.True(labels.Equal(lsA.MatchLabels(false, "che", "lol", "zimya"), lsC))
+}
+
+func (s *LabelsSuite) TestRange() {
+ lsMapA := map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ }
+ lsA := labels.FromMap(lsMapA)
+
+ lsMapB := make(map[string]string, len(lsMapA))
+ lsA.Range(func(l labels.Label) {
+ lsMapB[l.Name] = l.Value
+ })
+
+ s.Equal(lsMapA, lsMapB)
+}
+
+func (s *LabelsSuite) TestRenewSnapshot() {
+ lsMapA := map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ }
+
+ lsA := labels.FromMap(lsMapA)
+ lsaCopy := lsA.Copy()
+
+ s.True(lsA == lsaCopy)
+
+ for i := 0; i < 100; i++ {
+ labels.FromMap(map[string]string{
+ "__aaa__": fmt.Sprintf("11111%d", i),
+ "__name__": fmt.Sprintf("ubernameB%d", i),
+ "lol": fmt.Sprintf("kekB%d", i),
+ "che": fmt.Sprintf("bureckB%d", i),
+ "zimya": fmt.Sprintf("reckB%d", i),
+ })
+ }
+
+ lsA.RenewSnapshot()
+
+ s.False(lsA == lsaCopy)
+}
+
+func (s *LabelsSuite) TestValidate() {
+ lsMapA := map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ }
+ lsA := labels.FromMap(lsMapA)
+
+ errEnd := errors.New("end")
+ length := len(lsMapA)
+ lsMapB := make(map[string]string, length)
+ err := lsA.Validate(func(l labels.Label) error {
+ lsMapB[l.Name] = l.Value
+
+ length--
+ if length == 0 {
+ return errEnd
+ }
+ return nil
+ })
+
+ s.Equal(lsMapA, lsMapB)
+ s.ErrorIs(err, errEnd)
+}
+
+func (s *LabelsSuite) TestWithoutEmpty() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ lsB := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "lol", "",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ s.Equal(lsA.Bytes(nil), lsB.WithoutEmpty().Bytes(nil))
+ s.True(labels.Equal(lsA, lsB.WithoutEmpty()))
+}
+
+func (s *LabelsSuite) TestIsZero() {
+ lsA := labels.EmptyLabels()
+
+ s.True(lsA.IsZero())
+}
+
+func (s *LabelsSuite) TestIsZeroFalse() {
+ lsA := labels.FromStrings(
+ "__aaa__", "11111",
+ "__name__", "ubername",
+ "che", "bureck",
+ "zimya", "reck",
+ )
+
+ s.False(lsA.IsZero())
+}
+
+func (s *LabelsSuite) TestIsZeroFalseLSS() {
+ lss := cppbridge.NewLSSWithSnapshot(cppbridge.NewQueryableLssStorage())
+ lsid := lss.FindOrEmplace(model.LabelSetFromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ }))
+ lsA := labels.NewLabelsWithLSS(lss.Snapshot(), nil, lsid, 3)
+
+ s.False(lsA.IsZero())
+}
+
+func BenchmarkLabels_Bytes(b *testing.B) {
+ lsA := labels.FromMap(map[string]string{
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ buf := make([]byte, 48)
+ for i := 0; i < b.N; i++ {
+ buf = lsA.Bytes(buf)
+ }
+}
+
+func BenchmarkLabels_Range(b *testing.B) {
+ lsA := labels.FromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ for i := 0; i < b.N; i++ {
+ lsA.Range(func(l labels.Label) {
+ _ = l
+ })
+ }
+}
+
+func BenchmarkLabels_RangeDropMetricName(b *testing.B) {
+ lsA := labels.FromMap(map[string]string{
+ "__aaa__": "11111",
+ "__name__": "ubername",
+ "lol": "kek",
+ "che": "bureck",
+ "zimya": "reck",
+ })
+
+ for i := 0; i < b.N; i++ {
+ lsA.DropMetricName().Range(func(l labels.Label) {
+ _ = l
+ })
+ }
+}
diff --git a/model/labels/labels_stringlabels.go b/model/labels/labels_stringlabels.go
index 2bb8242e46..f825e208b8 100644
--- a/model/labels/labels_stringlabels.go
+++ b/model/labels/labels_stringlabels.go
@@ -505,6 +505,7 @@ func (b *Builder) Labels() Labels {
for ; a < len(b.add); a++ {
buf = appendLabelTo(buf, &b.add[a])
}
+
return Labels{data: yoloString(buf)}
}
@@ -656,6 +657,7 @@ func (b *ScratchBuilder) Labels() Labels {
marshalLabelsToSizedBuffer(b.add, buf)
b.output = Labels{data: yoloString(buf)}
}
+
return b.output
}
diff --git a/model/labels/labels_test.go b/model/labels/labels_test.go
index 3a4040776c..743c459db4 100644
--- a/model/labels/labels_test.go
+++ b/model/labels/labels_test.go
@@ -681,7 +681,8 @@ func BenchmarkLabels_Compare(b *testing.B) {
}
func TestLabels_Copy(t *testing.T) {
- require.Equal(t, FromStrings("aaa", "111", "bbb", "222"), FromStrings("aaa", "111", "bbb", "222").Copy())
+ // PP_CHANGES.md: rebuild on cpp
+ require.True(t, Equal(FromStrings("aaa", "111", "bbb", "222"), FromStrings("aaa", "111", "bbb", "222").Copy()))
}
func TestLabels_Map(t *testing.T) {
@@ -810,7 +811,7 @@ func TestBuilder(t *testing.T) {
b := NewBuilder(FromStrings("aaa", "111"))
b.Del("bbb")
b.Set("bbb", "222")
- require.Equal(t, FromStrings("aaa", "111", "bbb", "222"), b.Labels())
+ require.True(t, Equal(FromStrings("aaa", "111", "bbb", "222"), b.Labels())) // PP_CHANGES.md: rebuild on cpp
require.Equal(t, "222", b.Get("bbb"))
})
}
@@ -959,7 +960,7 @@ func TestMarshaling(t *testing.T) {
var gotJ Labels
err = json.Unmarshal(b, &gotJ)
require.NoError(t, err)
- require.Equal(t, lbls, gotJ)
+ require.True(t, Equal(lbls, gotJ))
expectedYAML := "aaa: \"111\"\nbbb: \"2222\"\nccc: \"33333\"\n"
b, err = yaml.Marshal(lbls)
@@ -969,7 +970,7 @@ func TestMarshaling(t *testing.T) {
var gotY Labels
err = yaml.Unmarshal(b, &gotY)
require.NoError(t, err)
- require.Equal(t, lbls, gotY)
+ require.True(t, Equal(lbls, gotY))
// Now in a struct with a tag
type foo struct {
@@ -985,7 +986,7 @@ func TestMarshaling(t *testing.T) {
var gotFJ foo
err = json.Unmarshal(b, &gotFJ)
require.NoError(t, err)
- require.Equal(t, f, gotFJ)
+ require.True(t, Equal(f.ALabels, gotFJ.ALabels)) // PP_CHANGES.md: rebuild on cpp
b, err = yaml.Marshal(f)
require.NoError(t, err)
@@ -995,5 +996,65 @@ func TestMarshaling(t *testing.T) {
var gotFY foo
err = yaml.Unmarshal(b, &gotFY)
require.NoError(t, err)
- require.Equal(t, f, gotFY)
+ require.True(t, Equal(f.ALabels, gotFY.ALabels)) // PP_CHANGES.md: rebuild on cpp
+}
+
+func BenchmarkLabels_Equals_Singe(b *testing.B) {
+ x := FromStringsForBenchmark("__name__", "kube_pod_container_status_last_terminated_exitcode", "cluster", "prod-af-north-0", " container", "prometheus", "instance", "kube-state-metrics-0:kube-state-metrics:ksm", "job", "kube-state-metrics/kube-state-metrics", " namespace", "observability-prometheus", "pod", "observability-prometheus-0", "uid", "d3ec90b2-4975-4607-b45d-b9ad64bb417e")
+ y := FromStringsForBenchmark("__name__", "kube_pod_container_status_last_terminated_exitcode", "cluster", "prod-af-north-0", " container", "prometheus", "instance", "kube-state-metrics-0:kube-state-metrics:ksm", "job", "kube-state-metrics/kube-state-metrics", " namespace", "observability-prometheus", "pod", "observability-prometheus-0", "uid", "deadbeef-0000-1111-2222-b9ad64bb417e")
+
+ var v string
+ buf := make([]byte, 1024)
+
+ for i := 0; i < b.N; i++ {
+ _ = Equal(x, y)
+
+ // x.Range(func(l Label) {
+ // v = l.Value
+ // })
+
+ // _ = x.Validate(func(l Label) error {
+ // v = l.Value
+
+ // return nil
+ // })
+
+ // x.DropMetricName()
+
+ // buf = x.BytesWithoutLabels(buf, "aaa", "bbb")
+
+ // x.Len()
+ }
+
+ _ = v
+ _ = buf
+}
+
+func BenchmarkLabels_Builders(b *testing.B) {
+ x := FromStringsForBenchmark("__name__", "kube_pod_container_status_last_terminated_exitcode", "cluster", "prod-af-north-0", " container", "prometheus", "instance", "kube-state-metrics-0:kube-state-metrics:ksm", "job", "kube-state-metrics/kube-state-metrics", " namespace", "observability-prometheus", "pod", "observability-prometheus-0", "uid", "d3ec90b2-4975-4607-b45d-b9ad64bb417e")
+ br := NewBuilder(x)
+
+ var v Labels
+
+ for i := 0; i < b.N; i++ {
+ br.Reset(x)
+ br.Set("aaa", "aaa")
+ v = br.Labels()
+ }
+
+ _ = v
+}
+
+func BenchmarkLabels_SBuilders(b *testing.B) {
+ br := NewScratchBuilder(1)
+
+ var v Labels
+
+ for i := 0; i < b.N; i++ {
+ br.Reset()
+ br.Add("aaa", "aaa")
+ v = br.Labels()
+ }
+
+ _ = v
}
diff --git a/model/labels/sharding.go b/model/labels/sharding.go
index 8b3a369397..1b24052ca4 100644
--- a/model/labels/sharding.go
+++ b/model/labels/sharding.go
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//go:build !stringlabels && !dedupelabels
+//go:build !stringlabels && !dedupelabels && !cpplabels
package labels
diff --git a/model/labels/sharding_labels_cpp.go b/model/labels/sharding_labels_cpp.go
new file mode 100644
index 0000000000..764e2198fc
--- /dev/null
+++ b/model/labels/sharding_labels_cpp.go
@@ -0,0 +1,31 @@
+//go:build cpplabels
+
+package labels
+
+import (
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
+
+ "github.com/cespare/xxhash/v2"
+)
+
+// StableHash is a labels hashing implementation which is guaranteed to not change over time.
+// This function should be used whenever labels hashing backward compatibility must be guaranteed.
+func StableHash(ls Labels) uint64 {
+ if ls.IsZero() {
+ return 0
+ }
+
+ // Use xxhash.Sum64(b) for fast path as it's faster.
+ b := make([]byte, 0, 1024) //revive:disable-line:add-constant // this is already constants
+
+ _ = ls.snapshot.RangeLabelSet(ls.id, ls.dropMetricName, func(l cppbridge.Label) error {
+ b = append(b, l.Name...)
+ b = append(b, seps[0])
+ b = append(b, l.Value...)
+ b = append(b, seps[0])
+
+ return nil
+ })
+
+ return xxhash.Sum64(b)
+}
diff --git a/notifier/notifier.go b/notifier/notifier.go
index 6340625538..0def81140b 100644
--- a/notifier/notifier.go
+++ b/notifier/notifier.go
@@ -40,6 +40,7 @@ import (
"github.com/prometheus/prometheus/discovery/targetgroup"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/relabel"
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
)
const (
@@ -102,6 +103,15 @@ func (a *Alert) ResolvedAt(ts time.Time) bool {
return !a.EndsAt.After(ts)
}
+// Equal asserts that two Alert are equal.
+func (a *Alert) Equal(other *Alert) bool {
+ return a.GeneratorURL == other.GeneratorURL &&
+ a.EndsAt.Equal(other.EndsAt) &&
+ a.StartsAt.Equal(other.StartsAt) &&
+ labels.Equal(a.Annotations, other.Annotations) &&
+ labels.Equal(a.Labels, other.Labels)
+}
+
// Manager is responsible for dispatching alert notifications to an
// alert manager service.
type Manager struct {
@@ -124,7 +134,7 @@ type Manager struct {
type Options struct {
QueueCapacity int
DrainOnShutdown bool
- ExternalLabels labels.Labels
+ ExternalLabels cppbridge.Labels // PP_CHANGES.md: rebuild on cpp
RelabelConfigs []*relabel.Config
// Used for sending HTTP requests to the Alertmanager.
Do func(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error)
@@ -450,13 +460,13 @@ func (n *Manager) Send(alerts ...*Alert) {
n.setMore()
}
-func relabelAlerts(relabelConfigs []*relabel.Config, externalLabels labels.Labels, alerts []*Alert) []*Alert {
+func relabelAlerts(relabelConfigs []*relabel.Config, externalLabels cppbridge.Labels, alerts []*Alert) []*Alert { // PP_CHANGES.md: rebuild on cpp
lb := labels.NewBuilder(labels.EmptyLabels())
var relabeledAlerts []*Alert
for _, a := range alerts {
lb.Reset(a.Labels)
- externalLabels.Range(func(l labels.Label) {
+ externalLabels.Range(func(l cppbridge.Label) { // PP_CHANGES.md: rebuild on cpp
if a.Labels.Get(l.Name) == "" {
lb.Set(l.Name, l.Value)
}
@@ -557,7 +567,7 @@ func (n *Manager) sendAll(alerts ...*Alert) bool {
}
if len(ams.cfg.AlertRelabelConfigs) > 0 {
- amAlerts = relabelAlerts(ams.cfg.AlertRelabelConfigs, labels.Labels{}, alerts)
+ amAlerts = relabelAlerts(ams.cfg.AlertRelabelConfigs, cppbridge.Labels{}, alerts) // PP_CHANGES.md: rebuild on cpp
if len(amAlerts) == 0 {
ams.mtx.RUnlock()
continue
diff --git a/notifier/notifier_test.go b/notifier/notifier_test.go
index cf922a537c..a8238e40ce 100644
--- a/notifier/notifier_test.go
+++ b/notifier/notifier_test.go
@@ -41,6 +41,7 @@ import (
"github.com/prometheus/prometheus/discovery/targetgroup"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/relabel"
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
)
func TestPostPath(t *testing.T) {
@@ -371,7 +372,7 @@ func TestCustomDo(t *testing.T) {
func TestExternalLabels(t *testing.T) {
h := NewManager(&Options{
QueueCapacity: 3 * maxBatchSize,
- ExternalLabels: labels.FromStrings("a", "b"),
+ ExternalLabels: cppbridge.FromStrings("a", "b"), // PP_CHANGES.md: rebuild on cpp
RelabelConfigs: []*relabel.Config{
{
SourceLabels: model.LabelNames{"alertname"},
diff --git a/pp-pkg/.golangci.yml b/pp-pkg/.golangci.yml
index 83c92b26b4..705f16332c 100644
--- a/pp-pkg/.golangci.yml
+++ b/pp-pkg/.golangci.yml
@@ -23,6 +23,8 @@ output:
# all available settings of specific linters
linters-settings:
govet:
+ # report about shadowed variables
+ check-shadowing: true
# settings per analyzer
settings:
printf: # analyzer name, run `go tool vet help` to see all analyzers
@@ -31,10 +33,10 @@ linters-settings:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
- shadow:
- # Whether to be strict about shadowing; can be noisy.
- # Default: false
- strict: true
+ # shadow:
+ # # Whether to be strict about shadowing; can be noisy.
+ # # Default: false
+ # strict: true
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
diff --git a/pp-pkg/rules/alerting.go b/pp-pkg/rules/alerting.go
index 2dc0917dce..074c94fd5e 100644
--- a/pp-pkg/rules/alerting.go
+++ b/pp-pkg/rules/alerting.go
@@ -30,6 +30,7 @@ import (
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/rulefmt"
"github.com/prometheus/prometheus/model/timestamp"
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/promql/parser"
"github.com/prometheus/prometheus/storage"
@@ -76,8 +77,9 @@ func (s AlertState) String() string {
type Alert struct {
State AlertState
- Labels labels.Labels
- Annotations labels.Labels
+ labels labels.Labels
+ annotations labels.Labels
+ labelsMtx sync.RWMutex // PP_CHANGES.md: rebuild on cpp
// The value at the last evaluation of the alerting expression.
Value float64
@@ -91,6 +93,56 @@ type Alert struct {
KeepFiringSince time.Time
}
+// NewAlert init new *Alert .
+func NewAlert(
+ ls, annotations labels.Labels,
+ activeAt, firedAt, resolvedAt, validUntil time.Time,
+) *Alert { // PP_CHANGES.md: rebuild on cpp
+ return &Alert{
+ labels: ls,
+ annotations: annotations,
+ ActiveAt: activeAt,
+ FiredAt: firedAt,
+ ResolvedAt: resolvedAt,
+ ValidUntil: validUntil,
+ }
+}
+
+// Annotations return alert annotations.
+func (a *Alert) Annotations() labels.Labels { // PP_CHANGES.md: rebuild on cpp
+ a.labelsMtx.RLock()
+ defer a.labelsMtx.RUnlock()
+
+ return a.annotations
+}
+
+// Labels return alert labels.
+func (a *Alert) Labels() labels.Labels { // PP_CHANGES.md: rebuild on cpp
+ a.labelsMtx.RLock()
+ defer a.labelsMtx.RUnlock()
+
+ return a.labels
+}
+
+// Copy make new copy Alert.
+func (a *Alert) Copy() *Alert { // PP_CHANGES.md: rebuild on cpp
+ a.labelsMtx.RLock()
+ defer a.labelsMtx.RUnlock()
+
+ return &Alert{
+ State: a.State,
+ labels: a.labels.Copy(),
+ annotations: a.annotations.Copy(),
+ Value: a.Value,
+ ActiveAt: a.ActiveAt,
+ FiredAt: a.FiredAt,
+ ResolvedAt: a.ResolvedAt,
+ LastSentAt: a.LastSentAt,
+ ValidUntil: a.ValidUntil,
+ KeepFiringSince: a.KeepFiringSince,
+ }
+}
+
func (a *Alert) needsSending(ts time.Time, resendDelay time.Duration) bool {
if a.State == StatePending {
return false
@@ -104,6 +156,14 @@ func (a *Alert) needsSending(ts time.Time, resendDelay time.Duration) bool {
return a.LastSentAt.Add(resendDelay).Before(ts)
}
+// renewLabelsSnapshot renew labels and annotations snapshots.
+func (a *Alert) renewLabelsSnapshot() { // PP_CHANGES.md: rebuild on cpp
+ a.labelsMtx.Lock()
+ a.labels.RenewSnapshot()
+ a.annotations.RenewSnapshot()
+ a.labelsMtx.Unlock()
+}
+
// An AlertingRule generates alerts from its vector expression.
type AlertingRule struct {
// The name of the alert.
@@ -120,6 +180,7 @@ type AlertingRule struct {
labels labels.Labels
// Non-identifying key/value pairs.
annotations labels.Labels
+ labelsMtx sync.RWMutex // PP_CHANGES.md: rebuild on cpp
// External labels from the global config.
externalLabels map[string]string
// The external URL from the --web.external-url flag.
@@ -150,7 +211,7 @@ type AlertingRule struct {
// NewAlertingRule constructs a new AlertingRule.
func NewAlertingRule(
name string, vec parser.Expr, hold, keepFiringFor time.Duration,
- labels, annotations, externalLabels labels.Labels, externalURL string,
+ ls, annotations labels.Labels, externalLabels cppbridge.Labels, externalURL string, // PP_CHANGES.md: on cpp
restored bool, logger log.Logger,
) *AlertingRule {
el := externalLabels.Map()
@@ -160,7 +221,7 @@ func NewAlertingRule(
vector: vec,
holdDuration: hold,
keepFiringFor: keepFiringFor,
- labels: labels,
+ labels: ls,
annotations: annotations,
externalLabels: el,
externalURL: externalURL,
@@ -219,18 +280,26 @@ func (r *AlertingRule) KeepFiringFor() time.Duration {
// Labels returns the labels of the alerting rule.
func (r *AlertingRule) Labels() labels.Labels {
+ r.labelsMtx.RLock() // PP_CHANGES.md: rebuild on cpp
+ defer r.labelsMtx.RUnlock() // PP_CHANGES.md: rebuild on cpp
+
return r.labels
}
// Annotations returns the annotations of the alerting rule.
func (r *AlertingRule) Annotations() labels.Labels {
+ r.labelsMtx.RLock() // PP_CHANGES.md: rebuild on cpp
+ defer r.labelsMtx.RUnlock() // PP_CHANGES.md: rebuild on cpp
+
return r.annotations
}
func (r *AlertingRule) sample(alert *Alert, ts time.Time) promql.Sample {
+ r.labelsMtx.RLock()
lb := labels.NewBuilder(r.labels)
+ r.labelsMtx.RUnlock()
- alert.Labels.Range(func(l labels.Label) {
+ alert.Labels().Range(func(l labels.Label) { // PP_CHANGES.md: rebuild on cpp
lb.Set(l.Name, l.Value)
})
@@ -246,13 +315,16 @@ func (r *AlertingRule) sample(alert *Alert, ts time.Time) promql.Sample {
return s
}
-// forStateSample returns a promql.Sample with the rule labels, `ALERTS_FOR_STATE` as the metric name and the rule name as the `alertname` label.
+// forStateSample returns a promql.Sample with the rule labels,
+// `ALERTS_FOR_STATE` as the metric name and the rule name as the `alertname` label.
// Optionally, if an alert is provided it'll copy the labels of the alert into the sample labels.
func (r *AlertingRule) forStateSample(alert *Alert, ts time.Time, v float64) promql.Sample {
+ r.labelsMtx.RLock() // PP_CHANGES.md: rebuild on cpp
lb := labels.NewBuilder(r.labels)
+ r.labelsMtx.RUnlock() // PP_CHANGES.md: rebuild on cpp
if alert != nil {
- alert.Labels.Range(func(l labels.Label) {
+ alert.Labels().Range(func(l labels.Label) { // PP_CHANGES.md: rebuild on cpp
lb.Set(l.Name, l.Value)
})
}
@@ -388,6 +460,9 @@ func (r *AlertingRule) Eval(ctx context.Context, queryOffset time.Duration, ts t
lb.Reset(smpl.Metric)
lb.Del(labels.MetricName)
+
+ r.labelsMtx.RLock() // PP_CHANGES.md: rebuild on cpp
+
r.labels.Range(func(l labels.Label) {
lb.Set(l.Name, expand(l.Value))
})
@@ -400,6 +475,9 @@ func (r *AlertingRule) Eval(ctx context.Context, queryOffset time.Duration, ts t
annotations := sb.Labels()
lbs := lb.Labels()
+
+ r.labelsMtx.RUnlock() // PP_CHANGES.md: rebuild on cpp
+
h := lbs.Hash()
resultFPs[h] = struct{}{}
@@ -408,8 +486,8 @@ func (r *AlertingRule) Eval(ctx context.Context, queryOffset time.Duration, ts t
}
alerts[h] = &Alert{
- Labels: lbs,
- Annotations: annotations,
+ labels: lbs, // PP_CHANGES.md: rebuild on cpp
+ annotations: annotations, // PP_CHANGES.md: rebuild on cpp
ActiveAt: ts,
State: StatePending,
Value: smpl.F,
@@ -424,7 +502,7 @@ func (r *AlertingRule) Eval(ctx context.Context, queryOffset time.Duration, ts t
// Update the last value and annotations if so, create a new alert entry otherwise.
if alert, ok := r.active[h]; ok && alert.State != StateInactive {
alert.Value = a.Value
- alert.Annotations = a.Annotations
+ alert.annotations = a.Annotations() // PP_CHANGES.md: rebuild on cpp
continue
}
@@ -483,8 +561,11 @@ func (r *AlertingRule) Eval(ctx context.Context, queryOffset time.Duration, ts t
}
if r.restored.Load() {
- vec = append(vec, r.sample(a, ts.Add(-queryOffset)))
- vec = append(vec, r.forStateSample(a, ts.Add(-queryOffset), float64(a.ActiveAt.Unix())))
+ vec = append(
+ vec,
+ r.sample(a, ts.Add(-queryOffset)),
+ r.forStateSample(a, ts.Add(-queryOffset), float64(a.ActiveAt.Unix())),
+ )
}
}
@@ -531,8 +612,7 @@ func (r *AlertingRule) currentAlerts() []*Alert {
alerts := make([]*Alert, 0, len(r.active))
for _, a := range r.active {
- anew := *a
- alerts = append(alerts, &anew)
+ alerts = append(alerts, a.Copy()) // PP_CHANGES.md: rebuild on cpp
}
return alerts
}
@@ -568,16 +648,18 @@ func (r *AlertingRule) sendAlerts(ctx context.Context, ts time.Time, resendDelay
delta = interval
}
alert.ValidUntil = ts.Add(4 * delta)
- anew := *alert
+ anew := alert.Copy() // PP_CHANGES.md: rebuild on cpp
// The notifier re-uses the labels slice, hence make a copy.
- anew.Labels = alert.Labels.Copy()
- alerts = append(alerts, &anew)
+ alerts = append(alerts, anew) // PP_CHANGES.md: rebuild on cpp
}
})
notifyFunc(ctx, r.vector.String(), alerts...)
}
func (r *AlertingRule) String() string {
+ r.labelsMtx.RLock() // PP_CHANGES.md: rebuild on cpp
+ defer r.labelsMtx.RUnlock() // PP_CHANGES.md: rebuild on cpp
+
ar := rulefmt.Rule{
Alert: r.name,
Expr: r.vector.String(),
@@ -594,3 +676,17 @@ func (r *AlertingRule) String() string {
return string(byt)
}
+
+// RenewLabelsSnapshot renew labels and annotations snapshots.
+func (r *AlertingRule) RenewLabelsSnapshot() { // PP_CHANGES.md: rebuild on cpp
+ r.labelsMtx.Lock()
+ r.labels.RenewSnapshot()
+ r.annotations.RenewSnapshot()
+ r.labelsMtx.Unlock()
+
+ r.activeMtx.Lock()
+ for _, a := range r.active {
+ a.renewLabelsSnapshot()
+ }
+ r.activeMtx.Unlock()
+}
diff --git a/pp-pkg/rules/alerting_test.go b/pp-pkg/rules/alerting_test.go
index 67d683c851..ccba524618 100644
--- a/pp-pkg/rules/alerting_test.go
+++ b/pp-pkg/rules/alerting_test.go
@@ -28,6 +28,7 @@ import (
"github.com/prometheus/prometheus/model/relabel"
"github.com/prometheus/prometheus/model/timestamp"
"github.com/prometheus/prometheus/notifier"
+ "github.com/prometheus/prometheus/pp/go/cppbridge" // PP_CHANGES.md: rebuild on cpp
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/promql/parser"
"github.com/prometheus/prometheus/promql/promqltest"
@@ -83,7 +84,7 @@ func TestAlertingRuleState(t *testing.T) {
}
for i, test := range tests {
- rule := NewAlertingRule(test.name, nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil)
+ rule := NewAlertingRule(test.name, nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil) // PP_CHANGES.md: rebuild on cpp
rule.active = test.active
got := rule.State()
require.Equal(t, test.want, got, "test case %d unexpected AlertState, want:%d got:%d", i, test.want, got)
@@ -122,7 +123,7 @@ func TestAlertingRuleTemplateWithHistogram(t *testing.T) {
time.Minute,
0,
labels.FromStrings("histogram", "{{ $value }}"),
- labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil, // PP_CHANGES.md: rebuild on cpp
)
evalTime := time.Now()
@@ -170,7 +171,7 @@ func TestAlertingRuleLabelsUpdate(t *testing.T) {
// If an alert is going back and forth between two label values it will never fire.
// Instead, you should write two alerts with constant labels.
labels.FromStrings("severity", "{{ if lt $value 80.0 }}critical{{ else }}warning{{ end }}"),
- labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil, // PP_CHANGES.md: rebuild on cpp
)
results := []promql.Vector{
@@ -274,7 +275,7 @@ func TestAlertingRuleExternalLabelsInTemplate(t *testing.T) {
0,
labels.FromStrings("templated_label", "There are {{ len $externalLabels }} external Labels, of which foo is {{ $externalLabels.foo }}."),
labels.EmptyLabels(),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
@@ -285,7 +286,7 @@ func TestAlertingRuleExternalLabelsInTemplate(t *testing.T) {
0,
labels.FromStrings("templated_label", "There are {{ len $externalLabels }} external Labels, of which foo is {{ $externalLabels.foo }}."),
labels.EmptyLabels(),
- labels.FromStrings("foo", "bar", "dings", "bums"),
+ cppbridge.FromStrings("foo", "bar", "dings", "bums"), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
@@ -369,7 +370,7 @@ func TestAlertingRuleExternalURLInTemplate(t *testing.T) {
0,
labels.FromStrings("templated_label", "The external URL is {{ $externalURL }}."),
labels.EmptyLabels(),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
@@ -380,7 +381,7 @@ func TestAlertingRuleExternalURLInTemplate(t *testing.T) {
0,
labels.FromStrings("templated_label", "The external URL is {{ $externalURL }}."),
labels.EmptyLabels(),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"http://localhost:1234",
true, log.NewNopLogger(),
)
@@ -464,7 +465,7 @@ func TestAlertingRuleEmptyLabelFromTemplate(t *testing.T) {
0,
labels.FromStrings("empty_label", ""),
labels.EmptyLabels(),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
@@ -525,7 +526,7 @@ instance: {{ $v.Labels.instance }}, value: {{ printf "%.0f" $v.Value }};
{{- end -}}
{{- end -}}
`),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
@@ -564,7 +565,7 @@ instance: {{ $v.Labels.instance }}, value: {{ printf "%.0f" $v.Value }};
func BenchmarkAlertingRuleAtomicField(b *testing.B) {
b.ReportAllocs()
- rule := NewAlertingRule("bench", nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil)
+ rule := NewAlertingRule("bench", nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil) // PP_CHANGES.md: rebuild on cpp
done := make(chan struct{})
go func() {
for i := 0; i < b.N; i++ {
@@ -605,7 +606,7 @@ func TestAlertingRuleDuplicate(t *testing.T) {
0,
labels.FromStrings("test", "test"),
labels.EmptyLabels(),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
@@ -649,7 +650,7 @@ func TestAlertingRuleLimit(t *testing.T) {
0,
labels.FromStrings("test", "test"),
labels.EmptyLabels(),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
@@ -721,7 +722,7 @@ func TestQueryForStateSeries(t *testing.T) {
time.Minute,
0,
labels.FromStrings("severity", "critical"),
- labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil, // PP_CHANGES.md: rebuild on cpp
)
sample := rule.forStateSample(nil, time.Time{}, 0)
@@ -753,13 +754,14 @@ func TestSendAlertsDontAffectActiveAlerts(t *testing.T) {
time.Minute,
0,
labels.FromStrings("severity", "critical"),
- labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil, // PP_CHANGES.md: rebuild on cpp
)
// Set an active alert.
lbls := labels.FromStrings("a1", "1")
h := lbls.Hash()
- al := &Alert{State: StateFiring, Labels: lbls, ActiveAt: time.Now()}
+ // PP_CHANGES.md: rebuild on cpp
+ al := &Alert{State: StateFiring, labels: lbls, ActiveAt: time.Now(), annotations: labels.EmptyLabels()}
rule.active[h] = al
expr, err := parser.ParseExpr("foo")
@@ -793,7 +795,7 @@ func TestSendAlertsDontAffectActiveAlerts(t *testing.T) {
// The relabel rule changes a1=1 to a1=bug.
// But the labels with the AlertingRule should not be changed.
- testutil.RequireEqual(t, labels.FromStrings("a1", "1"), rule.active[h].Labels)
+ testutil.RequireEqual(t, labels.FromStrings("a1", "1"), rule.active[h].Labels()) // PP_CHANGES.md: rebuild on cpp
}
func TestKeepFiringFor(t *testing.T) {
@@ -812,7 +814,7 @@ func TestKeepFiringFor(t *testing.T) {
time.Minute,
time.Minute,
labels.EmptyLabels(),
- labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil, // PP_CHANGES.md: rebuild on cpp
)
results := []promql.Vector{
@@ -923,7 +925,7 @@ func TestPendingAndKeepFiringFor(t *testing.T) {
time.Minute,
time.Minute,
labels.EmptyLabels(),
- labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil, // PP_CHANGES.md: rebuild on cpp
)
result := promql.Sample{
@@ -984,7 +986,7 @@ func TestAlertingEvalWithOrigin(t *testing.T) {
time.Minute,
lbs,
labels.EmptyLabels(),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
@@ -1006,7 +1008,7 @@ func TestAlertingRule_SetNoDependentRules(t *testing.T) {
0,
labels.FromStrings("test", "test"),
labels.EmptyLabels(),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
@@ -1027,7 +1029,7 @@ func TestAlertingRule_SetNoDependencyRules(t *testing.T) {
0,
labels.FromStrings("test", "test"),
labels.EmptyLabels(),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
@@ -1047,7 +1049,7 @@ func TestAlertingRule_ActiveAlertsCount(t *testing.T) {
time.Minute,
0,
labels.FromStrings("severity", "critical"),
- labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil, // PP_CHANGES.md: rebuild on cpp
)
require.Equal(t, 0, rule.ActiveAlertsCount())
@@ -1055,7 +1057,7 @@ func TestAlertingRule_ActiveAlertsCount(t *testing.T) {
// Set an active alert.
lbls := labels.FromStrings("a1", "1")
h := lbls.Hash()
- al := &Alert{State: StateFiring, Labels: lbls, ActiveAt: time.Now()}
+ al := &Alert{State: StateFiring, labels: lbls, ActiveAt: time.Now()}
rule.active[h] = al
require.Equal(t, 1, rule.ActiveAlertsCount())
diff --git a/pp-pkg/rules/group.go b/pp-pkg/rules/group.go
index 1074a62fb9..7880034e12 100644
--- a/pp-pkg/rules/group.go
+++ b/pp-pkg/rules/group.go
@@ -252,7 +252,6 @@ func (g *Group) run(ctx context.Context) {
level.Error(g.logger).Log("msg", "Failed to commit batch storage", "err", err)
return
}
-
}
}(time.Now())
}()
@@ -274,6 +273,8 @@ func (g *Group) run(ctx context.Context) {
}
evalTimestamp = evalTimestamp.Add((missed + 1) * g.interval)
g.evalIterationFunc(ctx, g, evalTimestamp)
+
+ g.renewLabelsSnapshot() // PP_CHANGES.md: rebuild on cpp
}
restoreStartTime := time.Now()
@@ -551,7 +552,8 @@ func (g *Group) RestoreForState(ts time.Time) {
"stage", "Select",
"err", err,
)
- // Even if we failed to query the `ALERT_FOR_STATE` series, we currently have no way to retry the restore process.
+ // Even if we failed to query the `ALERT_FOR_STATE` series,
+ // we currently have no way to retry the restore process.
// So the best we can do is mark the rule as restored and let it eventually fire.
alertRule.SetRestored(true)
continue
@@ -573,7 +575,7 @@ func (g *Group) RestoreForState(ts time.Time) {
alertRule.ForEachActiveAlert(func(a *Alert) {
var s storage.Series
- s, ok := seriesByLabels[a.Labels.String()]
+ s, ok := seriesByLabels[a.Labels().String()] // PP_CHANGES.md: rebuild on cpp
if !ok {
return
}
@@ -629,7 +631,7 @@ func (g *Group) RestoreForState(ts time.Time) {
a.ActiveAt = restoredActiveAt
level.Debug(g.logger).Log("msg", "'for' state restored",
labels.AlertName, alertRule.Name(), "restored_time", a.ActiveAt.Format(time.RFC850),
- "labels", a.Labels.String())
+ "labels", a.Labels().String()) // PP_CHANGES.md: rebuild on cpp
})
alertRule.SetRestored(true)
@@ -654,7 +656,8 @@ func (g *Group) Equals(ng *Group) bool {
return false
}
- if ((g.queryOffset == nil) != (ng.queryOffset == nil)) || (g.queryOffset != nil && ng.queryOffset != nil && *g.queryOffset != *ng.queryOffset) {
+ if ((g.queryOffset == nil) != (ng.queryOffset == nil)) ||
+ (g.queryOffset != nil && ng.queryOffset != nil && *g.queryOffset != *ng.queryOffset) {
return false
}
@@ -671,6 +674,13 @@ func (g *Group) Equals(ng *Group) bool {
return true
}
+// renewLabelsSnapshot renew labels and annotations snapshots.
+func (g *Group) renewLabelsSnapshot() { // PP_CHANGES.md: rebuild on cpp
+ for _, r := range g.rules {
+ r.RenewLabelsSnapshot()
+ }
+}
+
// GroupKey group names need not be unique across filenames.
func GroupKey(file, name string) string {
return file + ";" + name
@@ -884,7 +894,7 @@ func buildDependencyMap(rules []Rule) dependencyMap {
name := rule.Name()
outputs[name] = append(outputs[name], rule)
- parser.Inspect(rule.Query(), func(node parser.Node, path []parser.Node) error {
+ parser.Inspect(rule.Query(), func(node parser.Node, _ []parser.Node) error {
if n, ok := node.(*parser.VectorSelector); ok {
// A wildcard metric expression means we cannot reliably determine if this rule depends on any other,
// which means we cannot safely run any rules concurrently.
@@ -922,12 +932,12 @@ func buildDependencyMap(rules []Rule) dependencyMap {
}
// concurrencyEval evaluates the rules concurrently.
-func (g *Group) concurrencyEval(ctx context.Context, ts time.Time, bs storage.BatchStorage) float64 {
+func (g *Group) concurrencyEval(baseCtx context.Context, ts time.Time, bs storage.BatchStorage) float64 {
var (
samplesTotal atomic.Float64
wg sync.WaitGroup
mtx sync.Mutex
- concurrencyApp = bs.Appender(ctx)
+ concurrencyApp = bs.Appender(baseCtx)
queryFunc = g.opts.EngineQueryCtor(g.opts.Engine, g.opts.FanoutQueryable)
)
@@ -939,7 +949,7 @@ func (g *Group) concurrencyEval(ctx context.Context, ts time.Time, bs storage.Ba
}
logger := log.WithPrefix(g.logger, "name", rule.Name(), "index", i)
- ctx, sp := otel.Tracer("").Start(ctx, "rule")
+ ctx, sp := otel.Tracer("").Start(baseCtx, "rule")
sp.SetAttributes(attribute.String("name", rule.Name()))
defer func(t time.Time) {
sp.End()
@@ -1040,15 +1050,15 @@ func (g *Group) concurrencyEval(ctx context.Context, ts time.Time, bs storage.Ba
"err", err,
)
- return samplesTotal.Add(g.sequentiallyEval(ctx, ts, sequentiallyRules, bs))
+ return samplesTotal.Add(g.sequentiallyEval(baseCtx, ts, sequentiallyRules, bs))
}
- return samplesTotal.Add(g.sequentiallyEval(ctx, ts, sequentiallyRules, bs))
+ return samplesTotal.Add(g.sequentiallyEval(baseCtx, ts, sequentiallyRules, bs))
}
// sequentiallyEval evaluates the rules sequentially.
func (g *Group) sequentiallyEval(
- ctx context.Context,
+ baseCtx context.Context,
ts time.Time,
sequentiallyRules []Rule,
bs storage.BatchStorage,
@@ -1073,7 +1083,7 @@ func (g *Group) sequentiallyEval(
}
logger := log.WithPrefix(g.logger, "name", rule.Name(), "index", i)
- ctx, sp := otel.Tracer("").Start(ctx, "rule")
+ ctx, sp := otel.Tracer("").Start(baseCtx, "rule")
sp.SetAttributes(attribute.String("name", rule.Name()))
defer func(t time.Time) {
sp.End()
@@ -1115,13 +1125,13 @@ func (g *Group) sequentiallyEval(
app := bs.Appender(ctx)
defer func() {
- if err := app.Commit(); err != nil {
+ if errCommit := app.Commit(); errCommit != nil {
rule.SetHealth(HealthBad)
- rule.SetLastError(err)
- sp.SetStatus(codes.Error, err.Error())
+ rule.SetLastError(errCommit)
+ sp.SetStatus(codes.Error, errCommit.Error())
g.metrics.EvalFailures.WithLabelValues(GroupKey(g.File(), g.Name())).Inc()
- level.Warn(logger).Log("msg", "Rule sample appending failed", "err", err)
+ level.Warn(logger).Log("msg", "Rule sample appending failed", "err", errCommit)
return
}
}()
diff --git a/pp-pkg/rules/manager.go b/pp-pkg/rules/manager.go
index e33b0ceb0a..6fd38cb4b1 100644
--- a/pp-pkg/rules/manager.go
+++ b/pp-pkg/rules/manager.go
@@ -206,7 +206,7 @@ func (m *Manager) Stop() {
// Update the rule manager's state as the config requires. If
// loading the new rules failed the old rule set is restored.
// This method will no-op in case the manager is already stopped.
-func (m *Manager) Update(interval time.Duration, files []string, externalLabels labels.Labels, externalURL string, groupEvalIterationFunc GroupEvalIterationFunc) error {
+func (m *Manager) Update(interval time.Duration, files []string, externalLabels cppbridge.Labels, externalURL string, groupEvalIterationFunc GroupEvalIterationFunc) error {
m.mtx.Lock()
defer m.mtx.Unlock()
@@ -302,7 +302,11 @@ func (FileLoader) Parse(query string) (parser.Expr, error) { return parser.Parse
// LoadGroups reads groups from a list of files.
func (m *Manager) LoadGroups(
- interval time.Duration, externalLabels labels.Labels, externalURL string, groupEvalIterationFunc GroupEvalIterationFunc, filenames ...string,
+ interval time.Duration,
+ externalLabels cppbridge.Labels,
+ externalURL string,
+ groupEvalIterationFunc GroupEvalIterationFunc,
+ filenames ...string,
) (map[string]*Group, []error) {
groups := make(map[string]*Group)
@@ -436,8 +440,8 @@ func SendAlerts(s Sender, externalURL string) NotifyFunc {
for _, alert := range alerts {
a := ¬ifier.Alert{
StartsAt: alert.FiredAt,
- Labels: alert.Labels,
- Annotations: alert.Annotations,
+ Labels: alert.Labels(),
+ Annotations: alert.Annotations(),
GeneratorURL: externalURL + strutil.TableLinkForExpression(expr),
}
if !alert.ResolvedAt.IsZero() {
@@ -456,7 +460,7 @@ func SendAlerts(s Sender, externalURL string) NotifyFunc {
// RuleDependencyController controls whether a set of rules have dependencies between each other.
type RuleDependencyController interface {
- // AnalyseRules analyses dependencies between the input rules. For each rule that it's guaranteed
+ // AnalyseRules analyzes dependencies between the input rules. For each rule that it's guaranteed
// not having any dependants and/or dependency, this function should call Rule.SetNoDependentRules(true)
// and/or Rule.SetNoDependencyRules(true).
AnalyseRules(rules []Rule)
@@ -465,7 +469,7 @@ type RuleDependencyController interface {
type ruleDependencyController struct{}
// AnalyseRules implements RuleDependencyController.
-func (c ruleDependencyController) AnalyseRules(rules []Rule) {
+func (ruleDependencyController) AnalyseRules(rules []Rule) {
depMap := buildDependencyMap(rules)
for _, r := range rules {
r.SetNoDependentRules(depMap.dependents(r) == 0)
diff --git a/pp-pkg/rules/manager_test.go b/pp-pkg/rules/manager_test.go
index 4857759255..12beda5523 100644
--- a/pp-pkg/rules/manager_test.go
+++ b/pp-pkg/rules/manager_test.go
@@ -41,6 +41,7 @@ import (
"github.com/prometheus/prometheus/model/timestamp"
"github.com/prometheus/prometheus/model/value"
pp_pkg_storage "github.com/prometheus/prometheus/pp-pkg/storage"
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
pp_storage "github.com/prometheus/prometheus/pp/go/storage"
"github.com/prometheus/prometheus/pp/go/storage/catalog"
"github.com/prometheus/prometheus/promql"
@@ -74,7 +75,7 @@ func TestAlertingRule(t *testing.T) {
time.Minute,
0,
labels.FromStrings("severity", "{{\"c\"}}ritical"),
- labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil,
)
result := promql.Vector{
promql.Sample{
@@ -193,7 +194,7 @@ func TestAlertingRule(t *testing.T) {
prom_testutil.RequireEqual(t, test.result, filteredRes)
for _, aa := range rule.ActiveAlerts() {
- require.Zero(t, aa.Labels.Get(model.MetricNameLabel), "%s label set on active alert: %s", model.MetricNameLabel, aa.Labels)
+ require.Zero(t, aa.Labels().Get(model.MetricNameLabel), "%s label set on active alert: %s", model.MetricNameLabel, aa.Labels)
}
}
}
@@ -217,7 +218,7 @@ func TestForStateAddSamples(t *testing.T) {
time.Minute,
0,
labels.FromStrings("severity", "{{\"c\"}}ritical"),
- labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil,
)
result := promql.Vector{
promql.Sample{
@@ -346,7 +347,7 @@ func TestForStateAddSamples(t *testing.T) {
prom_testutil.RequireEqual(t, test.result, filteredRes)
for _, aa := range rule.ActiveAlerts() {
- require.Zero(t, aa.Labels.Get(model.MetricNameLabel), "%s label set on active alert: %s", model.MetricNameLabel, aa.Labels)
+ require.Zero(t, aa.Labels().Get(model.MetricNameLabel), "%s label set on active alert: %s", model.MetricNameLabel, aa.Labels)
}
}
})
@@ -356,7 +357,7 @@ func TestForStateAddSamples(t *testing.T) {
// sortAlerts sorts `[]*Alert` w.r.t. the Labels.
func sortAlerts(items []*Alert) {
sort.Slice(items, func(i, j int) bool {
- return labels.Compare(items[i].Labels, items[j].Labels) <= 0
+ return labels.Compare(items[i].Labels(), items[j].Labels()) <= 0
})
}
@@ -407,7 +408,7 @@ func TestForStateRestore(t *testing.T) {
alertForDuration,
0,
labels.FromStrings("severity", "critical"),
- labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil,
)
group, err := NewGroup(GroupOptions{
@@ -487,7 +488,7 @@ func TestForStateRestore(t *testing.T) {
alertForDuration,
0,
labels.FromStrings("severity", "critical"),
- labels.EmptyLabels(), labels.EmptyLabels(), "", false, nil,
+ labels.EmptyLabels(), cppbridge.EmptyLabels(), "", false, nil,
)
newGroup, err := NewGroup(GroupOptions{
Name: "default",
@@ -510,10 +511,10 @@ func TestForStateRestore(t *testing.T) {
got := newRule.ActiveAlerts()
for _, aa := range got {
- require.Zero(t, aa.Labels.Get(model.MetricNameLabel), "%s label set on active alert: %s", model.MetricNameLabel, aa.Labels)
+ require.Zero(t, aa.Labels().Get(model.MetricNameLabel), "%s label set on active alert: %s", model.MetricNameLabel, aa.Labels)
}
sort.Slice(got, func(i, j int) bool {
- return labels.Compare(got[i].Labels, got[j].Labels) < 0
+ return labels.Compare(got[i].Labels(), got[j].Labels()) < 0
})
// In all cases, we expect the restoration process to have completed.
@@ -538,7 +539,7 @@ func TestForStateRestore(t *testing.T) {
sortAlerts(exp)
sortAlerts(got)
for i, e := range exp {
- require.Equal(t, e.Labels, got[i].Labels)
+ require.True(t, labels.Equal(e.Labels(), got[i].Labels())) // PP_CHANGES.md: rebuild on cpp
// Difference in time should be within 1e6 ns, i.e. 1ms
// (due to conversion between ns & ms, float64 & int64).
@@ -685,7 +686,7 @@ groups:
})
require.NoError(t, err)
m.start()
- err = m.Update(time.Second, []string{fname}, labels.EmptyLabels(), "", nil)
+ err = m.Update(time.Second, []string{fname}, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
rgs := m.RuleGroups()
@@ -706,13 +707,13 @@ groups:
func TestCopyState(t *testing.T) {
oldGroup := &Group{
rules: []Rule{
- NewAlertingRule("alert", nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil),
+ NewAlertingRule("alert", nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil),
NewRecordingRule("rule1", nil, labels.EmptyLabels()),
NewRecordingRule("rule2", nil, labels.EmptyLabels()),
NewRecordingRule("rule3", nil, labels.FromStrings("l1", "v1")),
NewRecordingRule("rule3", nil, labels.FromStrings("l1", "v2")),
NewRecordingRule("rule3", nil, labels.FromStrings("l1", "v3")),
- NewAlertingRule("alert2", nil, 0, 0, labels.FromStrings("l2", "v1"), labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil),
+ NewAlertingRule("alert2", nil, 0, 0, labels.FromStrings("l2", "v1"), labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil),
},
evaluationTime: time.Second,
}
@@ -722,16 +723,15 @@ func TestCopyState(t *testing.T) {
NewRecordingRule("rule3", nil, labels.FromStrings("l1", "v0")),
NewRecordingRule("rule3", nil, labels.FromStrings("l1", "v1")),
NewRecordingRule("rule3", nil, labels.FromStrings("l1", "v2")),
- NewAlertingRule("alert", nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil),
+ NewAlertingRule("alert", nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil),
NewRecordingRule("rule1", nil, labels.EmptyLabels()),
- NewAlertingRule("alert2", nil, 0, 0, labels.FromStrings("l2", "v0"), labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil),
- NewAlertingRule("alert2", nil, 0, 0, labels.FromStrings("l2", "v1"), labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil),
+ NewAlertingRule("alert2", nil, 0, 0, labels.FromStrings("l2", "v0"), labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil),
+ NewAlertingRule("alert2", nil, 0, 0, labels.FromStrings("l2", "v1"), labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil),
NewRecordingRule("rule4", nil, labels.EmptyLabels()),
},
}
newGroup.CopyState(oldGroup)
- require.Equal(t, oldGroup.state, newGroup.state)
require.Equal(t, oldGroup.rules[0], newGroup.rules[3])
require.Equal(t, oldGroup.evaluationTime, newGroup.evaluationTime)
require.Equal(t, oldGroup.lastEvaluation, newGroup.lastEvaluation)
@@ -829,13 +829,13 @@ func TestUpdate(t *testing.T) {
ruleManager.start()
defer ruleManager.Stop()
- err = ruleManager.Update(10*time.Second, files, labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(10*time.Second, files, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
require.NotEmpty(t, ruleManager.groups, "expected non-empty rule groups")
ogs := make(map[string]*Group, len(ruleManager.groups))
maps.Copy(ogs, ruleManager.groups)
- err = ruleManager.Update(10*time.Second, files, labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(10*time.Second, files, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
for h, g := range ruleManager.groups {
require.Equal(t, ogs[h].state, g.state)
@@ -853,7 +853,7 @@ func TestUpdate(t *testing.T) {
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
- err = ruleManager.Update(10*time.Second, []string{tmpFile.Name()}, labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(10*time.Second, []string{tmpFile.Name()}, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
maps.Copy(ogs, ruleManager.groups)
@@ -929,7 +929,7 @@ func reloadAndValidate(rgs *rulefmt.RuleGroups, t *testing.T, tmpFile *os.File,
tmpFile.Seek(0, 0)
_, err = tmpFile.Write(bs)
require.NoError(t, err)
- err = ruleManager.Update(10*time.Second, []string{tmpFile.Name()}, labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(10*time.Second, []string{tmpFile.Name()}, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
for h, g := range ruleManager.groups {
if ogs[h] == g {
@@ -978,7 +978,7 @@ func TestNotify(t *testing.T) {
expr, err := parser.ParseExpr("a > 1")
require.NoError(t, err)
- rule := NewAlertingRule("aTooHigh", expr, 0, 0, labels.Labels{}, labels.Labels{}, labels.EmptyLabels(), "", true, log.NewNopLogger())
+ rule := NewAlertingRule("aTooHigh", expr, 0, 0, labels.Labels{}, labels.Labels{}, cppbridge.EmptyLabels(), "", true, log.NewNopLogger())
group, err := NewGroup(GroupOptions{
Name: "alert",
Interval: time.Second,
@@ -1102,7 +1102,7 @@ func TestMetricsUpdate(t *testing.T) {
}
for i, c := range cases {
- err := ruleManager.Update(time.Second, c.files, labels.EmptyLabels(), "", nil)
+ err := ruleManager.Update(time.Second, c.files, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
time.Sleep(2 * time.Second)
require.Equal(t, c.metrics, countMetrics(), "test %d: invalid count of metrics", i)
@@ -1191,7 +1191,7 @@ func TestGroupStalenessOnRemoval(t *testing.T) {
var totalStaleNaN int
for i, c := range cases {
- err := ruleManager.Update(time.Second, c.files, labels.EmptyLabels(), "", nil)
+ err := ruleManager.Update(time.Second, c.files, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
time.Sleep(3 * time.Second)
totalStaleNaN += c.staleNaN
@@ -1248,11 +1248,11 @@ func TestMetricsStalenessOnManagerShutdown(t *testing.T) {
}
}()
- err = ruleManager.Update(2*time.Second, files, labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(2*time.Second, files, cppbridge.EmptyLabels(), "", nil)
time.Sleep(4 * time.Second)
require.NoError(t, err)
start := time.Now()
- err = ruleManager.Update(3*time.Second, files[:0], labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(3*time.Second, files[:0], cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
ruleManager.Stop()
stopped = true
@@ -1295,7 +1295,7 @@ func TestGroupHasAlertingRules(t *testing.T) {
group: &Group{
name: "HasAlertingRule",
rules: []Rule{
- NewAlertingRule("alert", nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil),
+ NewAlertingRule("alert", nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), cppbridge.EmptyLabels(), "", true, nil),
NewRecordingRule("record", nil, labels.EmptyLabels()),
},
},
@@ -1620,7 +1620,7 @@ func TestManager_LoadGroups_ShouldCheckWhetherEachRuleHasDependentsAndDependenci
require.NoError(t, err)
t.Run("load a mix of dependent and independent rules", func(t *testing.T) {
- groups, errs := ruleManager.LoadGroups(time.Second, labels.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple.yaml"}...)
+ groups, errs := ruleManager.LoadGroups(time.Second, cppbridge.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple.yaml"}...)
require.Empty(t, errs)
require.Len(t, groups, 1)
@@ -1655,7 +1655,7 @@ func TestManager_LoadGroups_ShouldCheckWhetherEachRuleHasDependentsAndDependenci
})
t.Run("load only independent rules", func(t *testing.T) {
- groups, errs := ruleManager.LoadGroups(time.Second, labels.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple_independent.yaml"}...)
+ groups, errs := ruleManager.LoadGroups(time.Second, cppbridge.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple_independent.yaml"}...)
require.Empty(t, errs)
require.Len(t, groups, 1)
@@ -1679,7 +1679,7 @@ func TestDependencyMap(t *testing.T) {
expr, err = parser.ParseExpr("user:requests:rate1m <= 0")
require.NoError(t, err)
- rule2 := NewAlertingRule("ZeroRequests", expr, 0, 0, labels.Labels{}, labels.Labels{}, labels.EmptyLabels(), "", true, log.NewNopLogger())
+ rule2 := NewAlertingRule("ZeroRequests", expr, 0, 0, labels.Labels{}, labels.Labels{}, cppbridge.EmptyLabels(), "", true, log.NewNopLogger())
expr, err = parser.ParseExpr("sum by (user) (rate(requests[5m]))")
require.NoError(t, err)
@@ -1948,7 +1948,7 @@ func TestDependentRulesWithNonMetricExpression(t *testing.T) {
expr, err = parser.ParseExpr("user:requests:rate1m <= 0")
require.NoError(t, err)
- rule2 := NewAlertingRule("ZeroRequests", expr, 0, 0, labels.Labels{}, labels.Labels{}, labels.EmptyLabels(), "", true, log.NewNopLogger())
+ rule2 := NewAlertingRule("ZeroRequests", expr, 0, 0, labels.Labels{}, labels.Labels{}, cppbridge.EmptyLabels(), "", true, log.NewNopLogger())
expr, err = parser.ParseExpr("3")
require.NoError(t, err)
@@ -2009,7 +2009,7 @@ func TestDependencyMapUpdatesOnGroupUpdate(t *testing.T) {
ruleManager.start()
defer ruleManager.Stop()
- err = ruleManager.Update(10*time.Second, files, labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(10*time.Second, files, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
require.NotEmpty(t, ruleManager.groups, "expected non-empty rule groups")
@@ -2022,7 +2022,7 @@ func TestDependencyMapUpdatesOnGroupUpdate(t *testing.T) {
}
// Update once without changing groups.
- err = ruleManager.Update(10*time.Second, files, labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(10*time.Second, files, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
for h, g := range ruleManager.groups {
depMap := buildDependencyMap(g.rules)
@@ -2037,7 +2037,7 @@ func TestDependencyMapUpdatesOnGroupUpdate(t *testing.T) {
// Groups will be recreated when updated.
files[0] = "fixtures/rules_dependencies.yaml"
- err = ruleManager.Update(10*time.Second, files, labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(10*time.Second, files, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
for h, g := range ruleManager.groups {
@@ -2087,7 +2087,7 @@ func TestAsyncRuleEvaluation(t *testing.T) {
ruleManager, err := NewManager(optsFactory(fanoutStorage, adapter, &maxInflight, &inflightQueries, 0))
require.NoError(t, err)
- groups, errs := ruleManager.LoadGroups(time.Second, labels.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple.yaml"}...)
+ groups, errs := ruleManager.LoadGroups(time.Second, cppbridge.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple.yaml"}...)
require.Empty(t, errs)
require.Len(t, groups, 1)
@@ -2141,7 +2141,7 @@ func TestAsyncRuleEvaluation(t *testing.T) {
opts.ConcurrencyExecuter.Run()
defer opts.ConcurrencyExecuter.Stop()
- groups, errs := ruleManager.LoadGroups(time.Second, labels.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple.yaml"}...)
+ groups, errs := ruleManager.LoadGroups(time.Second, cppbridge.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple.yaml"}...)
require.Empty(t, errs)
require.Len(t, groups, 1)
@@ -2193,7 +2193,7 @@ func TestAsyncRuleEvaluation(t *testing.T) {
opts.ConcurrencyExecuter.Run()
defer opts.ConcurrencyExecuter.Stop()
- groups, errs := ruleManager.LoadGroups(time.Second, labels.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple_independent.yaml"}...)
+ groups, errs := ruleManager.LoadGroups(time.Second, cppbridge.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple_independent.yaml"}...)
require.Empty(t, errs)
require.Len(t, groups, 1)
@@ -2245,7 +2245,7 @@ func TestAsyncRuleEvaluation(t *testing.T) {
opts.ConcurrencyExecuter.Run()
defer opts.ConcurrencyExecuter.Stop()
- groups, errs := ruleManager.LoadGroups(time.Second, labels.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple_independent.yaml"}...)
+ groups, errs := ruleManager.LoadGroups(time.Second, cppbridge.EmptyLabels(), "", nil, []string{"fixtures/rules_multiple_independent.yaml"}...)
require.Empty(t, errs)
require.Len(t, groups, 1)
@@ -2293,7 +2293,7 @@ func TestBoundedRuleEvalConcurrency(t *testing.T) {
ruleManager, err := NewManager(optsFactory(fanoutStorage, adapter, &maxInflight, &inflightQueries, maxConcurrency))
require.NoError(t, err)
- groups, errs := ruleManager.LoadGroups(time.Second, labels.EmptyLabels(), "", nil, files...)
+ groups, errs := ruleManager.LoadGroups(time.Second, cppbridge.EmptyLabels(), "", nil, files...)
require.Empty(t, errs)
require.Len(t, groups, groupCount)
@@ -2326,13 +2326,13 @@ func TestUpdateWhenStopped(t *testing.T) {
})
require.NoError(t, err)
ruleManager.start()
- err = ruleManager.Update(10*time.Second, files, labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(10*time.Second, files, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
require.NotEmpty(t, ruleManager.groups)
ruleManager.Stop()
// Updates following a stop are no-op.
- err = ruleManager.Update(10*time.Second, []string{}, labels.EmptyLabels(), "", nil)
+ err = ruleManager.Update(10*time.Second, []string{}, cppbridge.EmptyLabels(), "", nil)
require.NoError(t, err)
}
diff --git a/pp-pkg/rules/origin_test.go b/pp-pkg/rules/origin_test.go
index 75c83f9a4e..00a1085665 100644
--- a/pp-pkg/rules/origin_test.go
+++ b/pp-pkg/rules/origin_test.go
@@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/prometheus/prometheus/model/labels"
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/promql/parser"
)
@@ -48,6 +49,7 @@ func (u unknownRule) SetNoDependentRules(bool) {}
func (u unknownRule) NoDependentRules() bool { return false }
func (u unknownRule) SetNoDependencyRules(bool) {}
func (u unknownRule) NoDependencyRules() bool { return false }
+func (u unknownRule) RenewLabelsSnapshot() {} // PP_CHANGES.md: rebuild on cpp
func TestNewRuleDetailPanics(t *testing.T) {
require.PanicsWithValue(t, `unknown rule type "rules.unknownRule"`, func() {
@@ -94,7 +96,7 @@ func TestNewRuleDetail(t *testing.T) {
0,
labels.FromStrings("test", "test"),
labels.EmptyLabels(),
- labels.EmptyLabels(),
+ cppbridge.EmptyLabels(), // PP_CHANGES.md: rebuild on cpp
"",
true, log.NewNopLogger(),
)
diff --git a/pp-pkg/rules/recording.go b/pp-pkg/rules/recording.go
index 17a75fdd1a..553a5fa732 100644
--- a/pp-pkg/rules/recording.go
+++ b/pp-pkg/rules/recording.go
@@ -17,6 +17,7 @@ import (
"context"
"fmt"
"net/url"
+ "sync"
"time"
"go.uber.org/atomic"
@@ -30,9 +31,10 @@ import (
// A RecordingRule records its vector expression into new timeseries.
type RecordingRule struct {
- name string
- vector parser.Expr
- labels labels.Labels
+ name string
+ vector parser.Expr
+ labels labels.Labels
+ labelsMtx sync.RWMutex // PP_CHANGES.md: rebuild on cpp
// The health of the recording rule.
health *atomic.String
// Timestamp of last evaluation of the recording rule.
@@ -73,6 +75,9 @@ func (rule *RecordingRule) Query() parser.Expr {
// Labels returns the rule labels.
func (rule *RecordingRule) Labels() labels.Labels {
+ rule.labelsMtx.RLock() // PP_CHANGES.md: rebuild on cpp
+ defer rule.labelsMtx.RUnlock() // PP_CHANGES.md: rebuild on cpp
+
return rule.labels
}
@@ -93,11 +98,13 @@ func (rule *RecordingRule) Eval(ctx context.Context, queryOffset time.Duration,
lb.Reset(sample.Metric)
lb.Set(labels.MetricName, rule.name)
+ rule.labelsMtx.RLock() // PP_CHANGES.md: rebuild on cpp
rule.labels.Range(func(l labels.Label) {
lb.Set(l.Name, l.Value)
})
sample.Metric = lb.Labels()
+ rule.labelsMtx.RUnlock() // PP_CHANGES.md: rebuild on cpp
}
// Check that the rule does not produce identical metrics after applying
@@ -117,6 +124,9 @@ func (rule *RecordingRule) Eval(ctx context.Context, queryOffset time.Duration,
}
func (rule *RecordingRule) String() string {
+ rule.labelsMtx.RLock() // PP_CHANGES.md: rebuild on cpp
+ defer rule.labelsMtx.RUnlock() // PP_CHANGES.md: rebuild on cpp
+
r := rulefmt.Rule{
Record: rule.name,
Expr: rule.vector.String(),
@@ -186,3 +196,10 @@ func (rule *RecordingRule) SetNoDependencyRules(noDependencyRules bool) {
func (rule *RecordingRule) NoDependencyRules() bool {
return rule.noDependencyRules.Load()
}
+
+// RenewLabelsSnapshot renew labels and annotations snapshots.
+func (rule *RecordingRule) RenewLabelsSnapshot() { // PP_CHANGES.md: rebuild on cpp
+ rule.labelsMtx.Lock()
+ rule.labels.RenewSnapshot()
+ rule.labelsMtx.Unlock()
+}
diff --git a/pp-pkg/rules/rule.go b/pp-pkg/rules/rule.go
index 687c03d000..4c1dd79dee 100644
--- a/pp-pkg/rules/rule.go
+++ b/pp-pkg/rules/rule.go
@@ -77,4 +77,7 @@ type Rule interface {
// any other rule in the group. In case this function returns false there's no such guarantee, which
// means the rule may or may not depend on other rules.
NoDependencyRules() bool
+
+ // RenewLabelsSnapshot renew labels and annotations snapshots.
+ RenewLabelsSnapshot() // PP_CHANGES.md: rebuild on cpp
}
diff --git a/pp-pkg/scrape/manager.go b/pp-pkg/scrape/manager.go
index 376fdea297..1d8c9dbf60 100644
--- a/pp-pkg/scrape/manager.go
+++ b/pp-pkg/scrape/manager.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"hash/fnv"
+ "io"
"reflect"
"sync"
"time"
@@ -17,7 +18,6 @@ import (
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/discovery/targetgroup"
- "github.com/prometheus/prometheus/model/labels"
pp_pkg_model "github.com/prometheus/prometheus/pp-pkg/model"
"github.com/prometheus/prometheus/pp/go/cppbridge"
"github.com/prometheus/prometheus/util/osutil"
@@ -43,6 +43,8 @@ type Adapter interface {
) (cppbridge.RelabelerStats, error)
}
+const defaultDurationRenew = 10 * time.Minute
+
// Options are the configuration parameters to the scrape manager.
type Options struct {
ExtraMetrics bool
@@ -89,6 +91,9 @@ type Manager struct {
bufferBuilders *buildersPool
bufferBatches *batchesPool
+ newScrapeFailureLogger func(string) (log.Logger, error)
+ scrapeFailureLoggers map[string]log.Logger
+
triggerReload chan struct{}
reportStatelessRelabeler *cppbridge.StatelessRelabeler
@@ -100,6 +105,7 @@ type Manager struct {
func NewManager(
o *Options,
logger log.Logger,
+ newScrapeFailureLogger func(string) (log.Logger, error),
adapter Adapter,
registerer prometheus.Registerer,
) (*Manager, error) {
@@ -124,12 +130,13 @@ func NewManager(
adapter: adapter,
opts: o,
logger: logger,
+ newScrapeFailureLogger: newScrapeFailureLogger,
scrapeConfigs: make(map[string]*config.ScrapeConfig),
scrapePools: make(map[string]*scrapePool),
graceShut: make(chan struct{}),
triggerReload: make(chan struct{}, 1),
metrics: sm,
- buffers: pool.New(1e3, 100e6, 2, func(sz int) interface{} { return make([]byte, 0, sz) }),
+ buffers: pool.New(1e3, 100e6, 3, func(sz int) any { return make([]byte, 0, sz) }),
bufferBuilders: newBuildersPool(),
bufferBatches: newbatchesPool(),
reportStatelessRelabeler: reportStatelessRelabeler,
@@ -172,8 +179,10 @@ func (m *Manager) reloader() {
}
ticker := time.NewTicker(time.Duration(reloadIntervalDuration))
+ timerRenew := time.NewTimer(defaultDurationRenew)
defer ticker.Stop()
+ defer timerRenew.Stop()
for {
select {
@@ -183,8 +192,12 @@ func (m *Manager) reloader() {
select {
case <-m.triggerReload:
m.reload()
+ timerRenew.Reset(defaultDurationRenew)
case <-m.graceShut:
return
+ case <-timerRenew.C:
+ m.reload()
+ timerRenew.Reset(defaultDurationRenew)
}
}
}
@@ -219,6 +232,14 @@ func (m *Manager) reload() {
continue
}
m.scrapePools[setName] = sp
+ if l, ok := m.scrapeFailureLoggers[scrapeConfig.ScrapeFailureLogFile]; ok {
+ sp.SetScrapeFailureLogger(l)
+ } else {
+ level.Error(sp.logger).Log(
+ "msg", "No logger found. This is a bug in Prometheus that should be reported upstream.",
+ "scrape_pool", setName,
+ )
+ }
}
wg.Add(1)
@@ -233,7 +254,7 @@ func (m *Manager) reload() {
}
// setOffsetSeed calculates a global offsetSeed per server relying on extra label set.
-func (m *Manager) setOffsetSeed(labels labels.Labels) error {
+func (m *Manager) setOffsetSeed(labels cppbridge.Labels) error {
h := fnv.New64a()
hostname, err := osutil.GetFQDN()
if err != nil {
@@ -277,8 +298,36 @@ func (m *Manager) ApplyConfig(cfg *config.Config) error {
for _, scfg := range scfgs {
c[scfg.JobName] = scfg
}
+ scrapeFailureLoggers := map[string]log.Logger{
+ "": nil, // Emptying the file name sets the scrape logger to nil.
+ }
+ for _, scfg := range scfgs {
+ c[scfg.JobName] = scfg
+ if _, ok := scrapeFailureLoggers[scfg.ScrapeFailureLogFile]; !ok {
+ // We promise to reopen the file on each reload.
+ var (
+ l log.Logger
+ err error
+ )
+ if m.newScrapeFailureLogger != nil {
+ if l, err = m.newScrapeFailureLogger(scfg.ScrapeFailureLogFile); err != nil {
+ return err
+ }
+ }
+ scrapeFailureLoggers[scfg.ScrapeFailureLogFile] = l
+ }
+ }
m.scrapeConfigs = c
+ oldScrapeFailureLoggers := m.scrapeFailureLoggers
+ for _, s := range oldScrapeFailureLoggers {
+ if closer, ok := s.(io.Closer); ok {
+ defer closer.Close()
+ }
+ }
+
+ m.scrapeFailureLoggers = scrapeFailureLoggers
+
if err := m.setOffsetSeed(cfg.GlobalConfig.ExternalLabels); err != nil {
return err
}
@@ -296,6 +345,16 @@ func (m *Manager) ApplyConfig(cfg *config.Config) error {
level.Error(m.logger).Log("msg", "error reloading scrape pool", "err", err, "scrape_pool", name)
failed = true
}
+ fallthrough
+ case ok:
+ if l, ok := m.scrapeFailureLoggers[cfg.ScrapeFailureLogFile]; ok {
+ sp.SetScrapeFailureLogger(l)
+ } else {
+ level.Error(sp.logger).Log(
+ "msg", "No logger found. This is a bug in Prometheus that should be reported upstream.",
+ "scrape_pool", name,
+ )
+ }
}
}
@@ -353,6 +412,7 @@ func (m *Manager) TargetsDropped() map[string][]*Target {
return targets
}
+// TargetsDroppedCounts returns the count of dropped targets from all scrape pools.
func (m *Manager) TargetsDroppedCounts() map[string]int {
m.mtxScrape.Lock()
defer m.mtxScrape.Unlock()
diff --git a/pp-pkg/scrape/pool.go b/pp-pkg/scrape/pool.go
index cc8b897004..16d6d5ea89 100644
--- a/pp-pkg/scrape/pool.go
+++ b/pp-pkg/scrape/pool.go
@@ -78,9 +78,10 @@ var errSampleLimit = errors.New("sample limit exceeded")
const maxAheadTime = 10 * time.Minute
+// Batch interface for batch time series.
type Batch interface {
// Add add to batch timeseries, timestamp and value.
- Add(builder *pp_model.LabelSetSimpleBuilder, timestamp uint64, value float64) error
+ Add(builder *pp_model.LabelSetSimpleBuilder, ts uint64, val float64) error
// Destroy destroy batch with destroyFunc(return to pool).
Destroy()
// IsEmpty check batch is empty.
@@ -95,7 +96,7 @@ type Batch interface {
func BatchWithLimit(batch Batch) Batch {
batch = &timeLimitBatch{
Batch: batch,
- maxTime: uint64(timestamp.FromTime(time.Now().Add(maxAheadTime))),
+ maxTime: uint64(timestamp.FromTime(time.Now().Add(maxAheadTime))), // #nosec G115 // no overflow
}
return batch
@@ -115,12 +116,12 @@ func newBatchTimeSeries() *BatchTimeSeries {
}
// Add add to batch timeseries, timestamp and value.
-func (batch *BatchTimeSeries) Add(builder *pp_model.LabelSetSimpleBuilder, timestamp uint64, val float64) error {
+func (batch *BatchTimeSeries) Add(builder *pp_model.LabelSetSimpleBuilder, ts uint64, val float64) error {
batch.data = append(
batch.data,
pp_model.TimeSeries{
LabelSet: builder.Build(),
- Timestamp: timestamp,
+ Timestamp: ts,
Value: val,
},
)
@@ -172,7 +173,7 @@ type samplesLimitBatch struct {
var _ Batch = (*samplesLimitBatch)(nil)
// Add add to batch timeseries, timestamp and value.
-func (b *samplesLimitBatch) Add(builder *pp_model.LabelSetSimpleBuilder, timestamp uint64, val float64) error {
+func (b *samplesLimitBatch) Add(builder *pp_model.LabelSetSimpleBuilder, ts uint64, val float64) error {
if !value.IsStaleNaN(val) {
b.i++
if b.i > b.limit {
@@ -180,7 +181,7 @@ func (b *samplesLimitBatch) Add(builder *pp_model.LabelSetSimpleBuilder, timesta
}
}
- return b.Batch.Add(builder, timestamp, val)
+ return b.Batch.Add(builder, ts, val)
}
// timeLimitBatch limits time on sample.
@@ -193,10 +194,39 @@ type timeLimitBatch struct {
var _ Batch = (*timeLimitBatch)(nil)
// Add add to batch timeseries, timestamp and value.
-func (b *timeLimitBatch) Add(builder *pp_model.LabelSetSimpleBuilder, timestamp uint64, val float64) error {
- if timestamp > b.maxTime {
+func (b *timeLimitBatch) Add(builder *pp_model.LabelSetSimpleBuilder, ts uint64, val float64) error {
+ if ts > b.maxTime {
return storage.ErrOutOfBounds
}
- return b.Batch.Add(builder, timestamp, val)
+ return b.Batch.Add(builder, ts, val)
}
+
+// //
+// // bufioPool
+// //
+
+// // poolBufio global pool *bufio.Reader.
+// var poolBufio = zeropool.New(func() *bufio.Reader { return bufio.NewReader(nr) })
+
+// //
+// // gziprPool
+// //
+
+// // poolGzipr global pool *gzip.Reader.
+// var poolGzipr = zeropool.New(func() *gzip.Reader { return &gzip.Reader{} })
+
+// //
+// // noopReader
+// //
+
+// // noopReader implementation io.Reader.
+// var nr = &noopReader{}
+
+// // noopReader implementation io.Reader.
+// type noopReader struct{}
+
+// // Read implementation io.Reader.
+// func (*noopReader) Read(_ []byte) (int, error) {
+// return 0, io.EOF
+// }
diff --git a/pp-pkg/scrape/scrape.go b/pp-pkg/scrape/scrape.go
index ada6f9cc21..1558cd6035 100644
--- a/pp-pkg/scrape/scrape.go
+++ b/pp-pkg/scrape/scrape.go
@@ -152,8 +152,14 @@ type scrapePool struct {
newLoop func(scrapeLoopOptions) loop
metrics *scrapeMetrics
noDefaultPort bool
+
+ scrapeFailureLogger log.Logger
+ scrapeFailureLoggerMtx sync.RWMutex
}
+// newScrapePool creates a new [scrapePool].
+//
+//revive:disable-next-line:function-length // init constructor.
func newScrapePool(
cfg *config.ScrapeConfig,
adapter Adapter,
@@ -235,8 +241,8 @@ func newScrapePool(
targetOptions.TargetLabels = append(
targetOptions.TargetLabels,
cppbridge.Label{
- Name: *((*string)(unsafe.Pointer(&l.Name))),
- Value: *((*string)(unsafe.Pointer(&l.Value))),
+ Name: strings.Clone(l.Name),
+ Value: strings.Clone(l.Value),
},
)
})
@@ -298,6 +304,29 @@ func (sp *scrapePool) DroppedTargetsCount() int {
return sp.droppedTargetsCount
}
+// SetScrapeFailureLogger sets the scrape failure logger for the scrape pool.
+func (sp *scrapePool) SetScrapeFailureLogger(l log.Logger) {
+ sp.scrapeFailureLoggerMtx.Lock()
+ defer sp.scrapeFailureLoggerMtx.Unlock()
+ if l != nil {
+ l = log.With(l, "job_name", sp.config.JobName)
+ }
+ sp.scrapeFailureLogger = l
+
+ sp.targetMtx.Lock()
+ defer sp.targetMtx.Unlock()
+ for _, s := range sp.loops {
+ s.setScrapeFailureLogger(sp.scrapeFailureLogger)
+ }
+}
+
+// getScrapeFailureLogger returns the scrape failure logger for the scrape pool.
+func (sp *scrapePool) getScrapeFailureLogger() log.Logger {
+ sp.scrapeFailureLoggerMtx.RLock()
+ defer sp.scrapeFailureLoggerMtx.RUnlock()
+ return sp.scrapeFailureLogger
+}
+
// stop terminates all scrape loops and returns after they all terminated.
func (sp *scrapePool) stop() {
sp.mtx.Lock()
@@ -450,6 +479,7 @@ func (sp *scrapePool) restartLoops(reuseCache bool) {
wg.Done()
newLoop.setForcedError(forcedErr)
+ newLoop.setScrapeFailureLogger(sp.getScrapeFailureLogger())
newLoop.run(nil)
}(oldLoop, newLoop)
@@ -488,6 +518,10 @@ func (sp *scrapePool) Sync(tgs []*targetgroup.Group) {
if len(failures) == 0 {
sp.targetsCache[tghash] = targets
}
+ } else {
+ for _, t := range targets {
+ t.RenewLabelsSnapshot()
+ }
}
for _, t := range targets {
// Replicate .Labels().IsEmpty() with a loop here to avoid generating garbage.
@@ -583,6 +617,7 @@ func (sp *scrapePool) sync(targets []*Target) {
if err != nil {
l.setForcedError(err)
}
+ l.setScrapeFailureLogger(sp.scrapeFailureLogger)
sp.activeTargets[hash] = t
sp.loops[hash] = l
@@ -595,7 +630,8 @@ func (sp *scrapePool) sync(targets []*Target) {
}
// Need to keep the most updated labels information
// for displaying it in the Service Discovery web page.
- sp.activeTargets[hash].SetDiscoveredLabels(t.DiscoveredLabels())
+ // sp.activeTargets[hash].SetDiscoveredLabels(t.DiscoveredLabels())
+ sp.activeTargets[hash].UpdateLabels(t)
}
}
@@ -656,6 +692,7 @@ var (
type scraper interface {
scrape(ctx context.Context) (*http.Response, error)
readResponse(ctx context.Context, resp *http.Response, w io.Writer) (string, error)
+ // readResponse2(ctx context.Context, resp *http.Response, buf *bytes.Buffer) (string, error)
Report(start time.Time, dur time.Duration, err error)
offset(interval time.Duration, offsetSeed uint64) time.Duration
String() string
@@ -782,6 +819,7 @@ func (s *targetScraper) String() string {
type loop interface {
run(errc chan<- error)
setForcedError(err error)
+ setScrapeFailureLogger(log.Logger)
stop()
getCache() *scrapeCache
disableEndOfRunStalenessMarkers()
@@ -791,6 +829,8 @@ type scrapeLoop struct {
scraper scraper
adapter Adapter
logger log.Logger
+ scrapeFailureLogger log.Logger
+ scrapeFailureLoggerMtx sync.RWMutex
state *cppbridge.StateV2
reportState *cppbridge.StateV2
cache *scrapeCache
@@ -859,7 +899,7 @@ func newScrapeLoop(
logger = log.NewNopLogger()
}
if buffers == nil {
- buffers = pool.New(1e3, 1e6, 2, func(sz int) interface{} { return make([]byte, 0, sz) })
+ buffers = pool.New(1e3, 1e6, 3, func(sz int) any { return make([]byte, 0, sz) })
}
if cache == nil {
cache = newScrapeCache(metrics)
@@ -922,6 +962,15 @@ func newScrapeLoop(
return sl
}
+func (sl *scrapeLoop) setScrapeFailureLogger(l log.Logger) {
+ sl.scrapeFailureLoggerMtx.Lock()
+ defer sl.scrapeFailureLoggerMtx.Unlock()
+ if ts, ok := sl.scraper.(fmt.Stringer); ok && l != nil {
+ l = log.With(l, "target", ts.String())
+ }
+ sl.scrapeFailureLogger = l
+}
+
func (sl *scrapeLoop) run(errc chan<- error) {
if !sl.skipOffsetting {
select {
@@ -1051,6 +1100,11 @@ func (sl *scrapeLoop) scrapeAndReport(last, appendTime time.Time, errc chan<- er
bytesRead = len(b)
} else {
level.Debug(sl.logger).Log("msg", "Scrape failed", "err", scrapeErr)
+ sl.scrapeFailureLoggerMtx.RLock()
+ if sl.scrapeFailureLogger != nil {
+ sl.scrapeFailureLogger.Log("err", scrapeErr)
+ }
+ sl.scrapeFailureLoggerMtx.RUnlock()
if errc != nil {
errc <- scrapeErr
}
@@ -1649,8 +1703,8 @@ func (c *scrapeCache) setHelp(metric, help []byte) {
e = &metaEntry{Metadata: metadata.Metadata{Type: model.MetricTypeUnknown}}
c.metadata[string(metric)] = e
}
- if e.Help != string(help) {
- e.Help = string(help)
+ if nhelp := string(help); e.Help != nhelp {
+ e.Help = nhelp
}
e.lastIter = c.iter
@@ -1719,3 +1773,51 @@ func (m *metaEntry) size() int {
// The attribute lastIter although part of the struct it is not metadata.
return len(m.Help) + len(m.Unit) + len(m.Type)
}
+
+// func (s *targetScraper) readResponse2(ctx context.Context, resp *http.Response, buf *bytes.Buffer) (string, error) {
+// defer func() {
+// io.Copy(io.Discard, resp.Body)
+// resp.Body.Close()
+// }()
+
+// if resp.StatusCode != http.StatusOK {
+// return "", fmt.Errorf("server returned HTTP status %s", resp.Status)
+// }
+
+// if s.bodySizeLimit <= 0 {
+// s.bodySizeLimit = math.MaxInt64
+// }
+// if resp.Header.Get("Content-Encoding") != "gzip" {
+// n, err := buf.ReadFrom(io.LimitReader(resp.Body, s.bodySizeLimit))
+// if err != nil {
+// return "", err
+// }
+// if n >= s.bodySizeLimit {
+// s.metrics.targetScrapeExceededBodySizeLimit.Inc()
+// return "", errBodySizeLimit
+// }
+// return resp.Header.Get("Content-Type"), nil
+// }
+
+// breader := poolBufio.Get()
+// defer poolBufio.Put(breader)
+// breader.Reset(resp.Body)
+
+// gzipr := poolGzipr.Get()
+// defer poolGzipr.Put(gzipr)
+// if err := gzipr.Reset(breader); err != nil {
+// return "", err
+// }
+
+// n, err := buf.ReadFrom(io.LimitReader(gzipr, s.bodySizeLimit))
+
+// gzipr.Close()
+// if err != nil {
+// return "", err
+// }
+// if n >= s.bodySizeLimit {
+// s.metrics.targetScrapeExceededBodySizeLimit.Inc()
+// return "", errBodySizeLimit
+// }
+// return resp.Header.Get("Content-Type"), nil
+// }
diff --git a/pp-pkg/scrape/target.go b/pp-pkg/scrape/target.go
index 80344a8bf1..f672802a30 100644
--- a/pp-pkg/scrape/target.go
+++ b/pp-pkg/scrape/target.go
@@ -190,6 +190,22 @@ func (t *Target) SetDiscoveredLabels(l labels.Labels) {
t.discoveredLabels = l
}
+// UpdateLabels update labels from other Target.
+func (t *Target) UpdateLabels(other *Target) {
+ t.mtx.Lock()
+ defer t.mtx.Unlock()
+ t.labels = other.labels
+ t.discoveredLabels = other.discoveredLabels
+}
+
+// RenewLabelsSnapshot renew labels and discoveredLabels snapshots.
+func (t *Target) RenewLabelsSnapshot() {
+ t.mtx.Lock()
+ defer t.mtx.Unlock()
+ t.labels.RenewSnapshot()
+ t.discoveredLabels.RenewSnapshot()
+}
+
// URL returns a copy of the target's URL.
func (t *Target) URL() *url.URL {
params := url.Values{}
@@ -485,6 +501,7 @@ func HashFromGroup(tg *targetgroup.Group) uint64 {
for i := range tg.Targets {
writeLabelSet(tg.Targets[i])
}
+ _, _ = res.WriteString(tg.Source)
return res.Sum64()
}
diff --git a/pp-pkg/storage/adapter.go b/pp-pkg/storage/adapter.go
index 34815c5cfc..03443d8191 100644
--- a/pp-pkg/storage/adapter.go
+++ b/pp-pkg/storage/adapter.go
@@ -8,6 +8,7 @@ import (
"github.com/jonboulle/clockwork"
"github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/pp-pkg/model"
"github.com/prometheus/prometheus/pp/go/cppbridge"
"github.com/prometheus/prometheus/pp/go/hatracker"
@@ -262,6 +263,46 @@ func (ar *Adapter) Close() error {
return nil
}
+// FindByHash label set by hash in cache.
+func (ar *Adapter) FindByHash(
+ builderSortedAdd []cppbridge.Label,
+ builderSortedDel []string,
+ builderSnapshot *cppbridge.LabelSetSnapshot,
+ hash uint64,
+ builderLSID uint32,
+) (labels.Labels, bool) {
+ return querier.FindByHash(
+ ar.proxy.Get(),
+ builderSortedAdd,
+ builderSortedDel,
+ builderSnapshot,
+ hash,
+ builderLSID,
+ )
+}
+
+// FindFromBuilder label set from builder in lss, if not found return EmptyLabels.
+//
+//revive:disable-next-line:flag-parameter this is not a flag, but a parameter
+func (ar *Adapter) FindFromBuilder(
+ builderSortedAdd []cppbridge.Label,
+ builderSortedDel []string,
+ builderSnapshot *cppbridge.LabelSetSnapshot,
+ hash uint64,
+ builderLSID uint32,
+ skipCache bool,
+) (labels.Labels, bool) {
+ return querier.FindFromBuilder(
+ ar.proxy.Get(),
+ builderSortedAdd,
+ builderSortedDel,
+ builderSnapshot,
+ hash,
+ builderLSID,
+ skipCache,
+ )
+}
+
// HeadQuerier returns [storage.Querier] from active head.
func (ar *Adapter) HeadQuerier(mint, maxt int64) (storage.Querier, error) {
return querier.NewQuerier(
diff --git a/pp-pkg/storage/batch_storage.go b/pp-pkg/storage/batch_storage.go
index 579c09b2c9..aeefdac15b 100644
--- a/pp-pkg/storage/batch_storage.go
+++ b/pp-pkg/storage/batch_storage.go
@@ -80,6 +80,7 @@ func (bs *BatchStorage) Commit(ctx context.Context) error {
}
_, err := bs.adapter.AppendTimeSeries(ctx, bs.batch, bs.state, false)
+ _ = bs.transactionHead.Close()
return err
}
diff --git a/pp-pkg/storage/remote/remote_read.go b/pp-pkg/storage/remote/remote_read.go
index fa812c6e2d..56fa048296 100644
--- a/pp-pkg/storage/remote/remote_read.go
+++ b/pp-pkg/storage/remote/remote_read.go
@@ -13,6 +13,7 @@ import (
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/model/labels"
+ "github.com/prometheus/prometheus/pp/go/cppbridge"
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/storage/remote"
"github.com/prometheus/prometheus/util/logging"
@@ -86,7 +87,7 @@ func (s *RemoteRead) ApplyConfig(conf *config.Config) error {
externalLabels := conf.GlobalConfig.ExternalLabels
if !rrConf.FilterExternalLabels {
- externalLabels = labels.EmptyLabels()
+ externalLabels = cppbridge.EmptyLabels() // PP_CHANGES.md: rebuild on cpp
}
queryables = append(queryables, remote.NewSampleAndChunkQueryableClient(
c,
diff --git a/pp/.golangci.yml b/pp/.golangci.yml
index 78b6b6532a..38583c7016 100644
--- a/pp/.golangci.yml
+++ b/pp/.golangci.yml
@@ -36,10 +36,10 @@ linters-settings:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
- shadow:
- # Whether to be strict about shadowing; can be noisy.
- # Default: false
- strict: true
+ # shadow:
+ # # Whether to be strict about shadowing; can be noisy.
+ # # Default: false
+ # strict: true
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
diff --git a/pp/bare_bones/algorithm.h b/pp/bare_bones/algorithm.h
index fc650bbfaf..4cccee6c66 100644
--- a/pp/bare_bones/algorithm.h
+++ b/pp/bare_bones/algorithm.h
@@ -21,12 +21,20 @@ constexpr bool is_in(const Value& value, Args&&... args) {
}
template
-auto lexicographical_compare_three_way(const Range1& range1, const Range2& range2, Comparator&& comparator) {
+auto lexicographical_compare_three_way(const Range1& range1, const Range2& range2, bool drop_metric_name_a, bool drop_metric_name_b, Comparator&& comparator) {
using result_type = decltype(comparator(*range1.begin(), *range2.begin()));
auto it_a = range1.begin();
auto it_b = range2.begin();
for (; it_a != range1.end() && it_b != range2.end(); ++it_a, ++it_b) {
+ if (drop_metric_name_a && (*it_a).first == "__name__") [[unlikely]] {
+ ++it_a;
+ }
+
+ if (drop_metric_name_b && (*it_b).first == "__name__") [[unlikely]] {
+ ++it_b;
+ }
+
if (const auto result = comparator(*it_a, *it_b); !std::is_eq(result)) {
return result;
}
diff --git a/pp/bare_bones/bitset.h b/pp/bare_bones/bitset.h
index b538ed6da7..2ffa8fd1e3 100644
--- a/pp/bare_bones/bitset.h
+++ b/pp/bare_bones/bitset.h
@@ -115,17 +115,21 @@ class Bitset {
PROMPP_ALWAYS_INLINE void reset(std::initializer_list values) noexcept { reset(values.begin(), values.end()); }
+ PROMPP_ALWAYS_INLINE void reset_all() noexcept {
+ if (size() != 0) {
+ const uint64_t size_in_uint64_elements = (size() + 63) >> 6;
+ assert(size_in_uint64_elements <= data_.size());
+ std::memset(data_, 0, size_in_uint64_elements << 3);
+ }
+ }
+
PROMPP_ALWAYS_INLINE bool operator[](uint32_t v) const noexcept {
assert(v < size());
return (data_[v >> 6] & (1ull << (v & 0x3F))) > 0;
}
void clear() noexcept {
- if (size() != 0) {
- const uint64_t size_in_uint64_elements = (size() + 63) >> 6;
- assert(size_in_uint64_elements <= data_.size());
- std::memset(data_, 0, size_in_uint64_elements << 3);
- }
+ reset_all();
set_size(0);
}
diff --git a/pp/entrypoint/common.cpp b/pp/entrypoint/common.cpp
index cbe4392f97..dd68c9ea7a 100644
--- a/pp/entrypoint/common.cpp
+++ b/pp/entrypoint/common.cpp
@@ -69,3 +69,5 @@ extern "C" void prompp_dump_memory_profile([[maybe_unused]] void* args, void* re
out->error = ENODATA;
#endif
}
+
+extern "C" void unsafe_call_2(void* args [[maybe_unused]], void* res [[maybe_unused]]) {}
diff --git a/pp/entrypoint/label_set.cpp b/pp/entrypoint/label_set.cpp
index c87a1db660..2318f5a370 100644
--- a/pp/entrypoint/label_set.cpp
+++ b/pp/entrypoint/label_set.cpp
@@ -5,32 +5,53 @@
#include "entrypoint/head/lss.h"
#include "primitives/go_model.h"
#include "primitives/go_slice.h"
+#include "prometheus/value.h"
using entrypoint::head::LssVariantPtr;
using PromPP::Primitives::Go::Slice;
using PromPP::Primitives::Go::SliceView;
-void prompp_label_set_length(void* args, void* res) {
+extern "C" void prompp_label_set_length(void* args, void* res) {
struct Arguments {
LssVariantPtr lss;
uint32_t series_id;
+ bool drop_metric_name;
};
struct Result {
size_t length;
};
auto in = static_cast(args);
+ auto out = new (res) Result();
- std::visit([in, res](auto& lss) { new (res) Result{.length = lss[in->series_id].size()}; }, *in->lss);
+ std::visit(
+ [in, out](auto& lss) {
+ const auto& ls = lss[in->series_id];
+ const auto size_of_ls = ls.size();
+
+ if (in->drop_metric_name && size_of_ls != 0) {
+ for (const auto& label : ls) {
+ if (label.first == PromPP::Prometheus::kMetricLabelName) [[unlikely]] {
+ continue;
+ }
+
+ ++out->length;
+ }
+ } else {
+ out->length = ls.size();
+ }
+ },
+ *in->lss);
}
-void prompp_label_set_serialize(void* args, void* res) {
+extern "C" void prompp_label_set_serialize(void* args, void* res) {
using PromPP::Primitives::Go::Label;
using PromPP::Primitives::Go::String;
struct Arguments {
LssVariantPtr lss;
uint32_t series_id;
+ bool drop_metric_name;
};
struct Result {
Slice