You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2026/06/10 15:43:35 UTC
(skywalking) branch swip-15-banyandb-so11y-rules created (now 6a96f7f19c)
This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a change to branch swip-15-banyandb-so11y-rules
in repository https://gitbox.apache.org/repos/asf/skywalking.git
at 6a96f7f19c SWIP-15: implement BanyanDB self-observability (cluster / container / group model)
This branch includes the following new commits:
new 6a96f7f19c SWIP-15: implement BanyanDB self-observability (cluster / container / group model)
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(skywalking) 01/01: SWIP-15: implement BanyanDB self-observability (cluster / container / group model)
Posted by wu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch swip-15-banyandb-so11y-rules
in repository https://gitbox.apache.org/repos/asf/skywalking.git
commit 6a96f7f19c27da296c494e90a990a06a2b600f41
Author: Wu Sheng <wu...@foxmail.com>
AuthorDate: Wed Jun 10 23:43:27 2026 +0800
SWIP-15: implement BanyanDB self-observability (cluster / container / group model)
Rebuild otel-rules/banyandb around the cluster reality: Service = cluster,
ServiceInstance = container (pod_name + container_name, with role/tier attributes),
Endpoint = group. Add banyandb-endpoint.yaml; redesign service/instance rules to
mirror the upstream FODC-proxy Grafana boards. Requires BanyanDB 0.11+.
Rewrite the e2e to a no-FODC file-discovery cluster (1 liaison + 1 hot data node);
the collector scrapes each node's :2121 directly and injects the identity labels.
Operator docs rewritten to the cluster/container/group model.
Validated: DSLClassGeneratorTest compiles all rules via the production path;
the e2e passes 16/16 against BanyanDB 0.11.
Co-Authored-By: Claude Fable 5 <no...@anthropic.com>
---
docs/en/banyandb/dashboards-banyandb.md | 176 +++++++++++++----
docs/en/changes/changes.md | 8 +
.../otel-rules/banyandb/banyandb-endpoint.yaml | 96 +++++++++
.../otel-rules/banyandb/banyandb-instance.yaml | 217 ++++++++++++++-------
.../otel-rules/banyandb/banyandb-service.yaml | 113 +++++------
test/e2e-v2/cases/banyandb/banyandb-cases.yaml | 63 ++++--
test/e2e-v2/cases/banyandb/docker-compose.yml | 64 ++++--
test/e2e-v2/cases/banyandb/e2e.yaml | 5 +-
.../metrics-has-label-value.yml} | 57 +++---
.../{otel-collector-config.yaml => nodes.yaml} | 39 +---
.../cases/banyandb/otel-collector-config.yaml | 30 ++-
11 files changed, 589 insertions(+), 279 deletions(-)
diff --git a/docs/en/banyandb/dashboards-banyandb.md b/docs/en/banyandb/dashboards-banyandb.md
index 1d07018f41..33ee16606f 100644
--- a/docs/en/banyandb/dashboards-banyandb.md
+++ b/docs/en/banyandb/dashboards-banyandb.md
@@ -1,49 +1,141 @@
-# BanyanDB self observability dashboard
+# BanyanDB self-observability dashboard
-[BanyanDB](https://skywalking.apache.org/docs/skywalking-banyandb/next/readme/), as an observability database, aims to ingest, analyze and store Metrics, Tracing, and Logging data. It's designed to handle observability data generated by **Apache SkyWalking**,it also provides a dashboard to visualize the self-observability metrics.
+[Apache SkyWalking BanyanDB](https://skywalking.apache.org/docs/skywalking-banyandb/next/readme/) is the
+native storage for SkyWalking. A production deployment is one **cluster** made of many **nodes**, each
+running one or more **containers** with a role (`liaison` front door, `data` backend, and the `lifecycle`
+tier-migration sidecar), and data is organized into **groups**. SkyWalking models that reality directly
+and renders it on the `Layer: BANYANDB` dashboards in the Horizon UI:
+
+| SkyWalking entity | BanyanDB concept | Identity |
+| ----------------- | ---------------- | -------- |
+| `Service` | one BanyanDB **cluster** | the `cluster` label |
+| `ServiceInstance` | one **container** on a node | `pod_name` + `container_name` (joined by `@`) |
+| ↳ attributes | role / tier | `container_name` (`liaison`/`data`/`lifecycle`), `node_type` (`hot`/`warm`/`cold`), `node_role`, `pod_name` |
+| `Endpoint` | one **group** (storage partition) | the `group` label (e.g. `sw_metricsMinute`) |
+
+> **Requires BanyanDB 0.11+.** This feature reads the FODC-proxy cluster-observability metric families
+> and the queue / lifecycle metric families that BanyanDB introduced after 0.10. Run a 0.11+ cluster
+> with the FODC proxy and the Prometheus metrics provider enabled.
## Data flow
-1. [BanyanDB](https://skywalking.apache.org/docs/skywalking-banyandb/next/readme/) collects metrics data internally and exposes a Prometheus http endpoint to retrieve the metrics.
-2. OpenTelemetry Collector fetches metrics from BanyanDB and pushes metrics to SkyWalking OAP Server via OpenTelemetry gRPC exporter.
-3. The SkyWalking OAP Server parses the expression with [MAL](../concepts-and-designs/mal.md) to filter/calculate/aggregate and store the results.
+
+1. Each BanyanDB container exposes its metrics; in a cluster the
+ [FODC proxy](https://skywalking.apache.org/docs/skywalking-banyandb/next/operation/fodc/overview/)
+ aggregates every container's Prometheus metrics onto a single `/metrics` endpoint (default `:17913`)
+ and stamps each sample with per-container identity labels (`pod_name`, `container_name`, `node_role`,
+ and `node_type` on data containers).
+2. An OpenTelemetry Collector scrapes the FODC proxy `/metrics` as the single Prometheus target, adds a
+ static `cluster: <name>` label (the only label SkyWalking must inject), and pushes via the
+ OpenTelemetry gRPC exporter to the SkyWalking OAP Server.
+3. The OAP Server parses the [MAL](../concepts-and-designs/mal.md) rules under `otel-rules/banyandb/` to
+ filter / calculate / aggregate and store the cluster, instance and group metrics.
## Set up
-1. Start [BanyanDB](https://skywalking.apache.org/docs/skywalking-banyandb/next/readme/),supporting both [Standalone Mode](https://skywalking.apache.org/docs/skywalking-banyandb/next/installation/standalone/) and [Cluster Mode](https://skywalking.apache.org/docs/skywalking-banyandb/next/installation/cluster/).
-2. Set up [OpenTelemetry Collector ](https://opentelemetry.io/docs/collector/getting-started/#docker). For details on Prometheus Receiver in OpenTelemetry Collector, refer to [here](../../../test/e2e-v2/cases/banyandb/otel-collector-config.yaml).
-3. Config SkyWalking [OpenTelemetry receiver](https://skywalking.apache.org/docs/main/next/en/setup/backend/opentelemetry-receiver/).
-
-## BanyanDB monitoring
-Self observability monitoring provides monitoring of the status and resources of the [BanyanDB](https://skywalking.apache.org/docs/skywalking-banyandb/next/readme/) server itself. `banyandb-server` is a `Service` in BanyanDB, and land on the `Layer: BANYANDB`.
-
-### Self observability metrics
-
-| Unit | Metric Name | Description | Data Source |
-|------|---------------------------------------------------|-------------|-------------|
-| o/s | meter_banyandb_write_rate | Write Rate (Operations per Second) | BanyanDB |
-| GiB | meter_banyandb_total_memory | Total Memory | BanyanDB |
-| GiB | meter_banyandb_disk_usage | Disk Usage | BanyanDB |
-| r/s | meter_banyandb_query_rate | Query Rate (Requests per Second) | BanyanDB |
-| Count | meter_banyandb_total_cpu | Total CPU Cores | BanyanDB |
-| c/m | meter_banyandb_write_and_query_errors_rate | Write and Query Errors Rate(Counts per Minute) | BanyanDB |
-| c/s | meter_banyandb_etcd_operation_rate | Etcd Operation Rate(Counts per Second) | BanyanDB |
-| Count | meter_banyandb_active_instance | Active Instances | BanyanDB |
-| % | meter_banyandb_cpu_usage | CPU Usage Percentage | BanyanDB |
-| % | meter_banyandb_rss_memory_usage | RSS Memory Usage Percentage | BanyanDB |
-| % | meter_banyandb_disk_usage_all | Disk Usage Percentage | BanyanDB |
-| KiB/s | meter_banyandb_network_usage_recv | Network Receive Rate | BanyanDB |
-| KiB/s | meter_banyandb_network_usage_sent | Network Send Rate | BanyanDB |
-| o/s | meter_banyandb_storage_write_rate | Storage Write Rate (Operations per Second) | BanyanDB |
-| s | meter_banyandb_query_latency | Query Latency (s) | BanyanDB |
-| Count | meter_banyandb_total_data | Total Data Elements | BanyanDB |
-| r/m | meter_banyandb_merge_file_data | Merge File Data Rate(Revolutions per Minute) | BanyanDB |
-| s | meter_banyandb_merge_file_latency | Merge File Latency(s) | BanyanDB |
-| Count | meter_banyandb_merge_file_partitions | Merge File Partitions | BanyanDB |
-| o/s | meter_banyandb_series_write_rate | Series Write Rate (Operations per Second) | BanyanDB |
-| o/s | meter_banyandb_series_term_search_rate | Series Term Search Rate (Operations per Second) | BanyanDB |
-| Count | meter_banyandb_total_series | Total Series Count | BanyanDB |
-| ops | meter_banyandb_stream_write_rate | Stream Write Rate (Operations per Second) | BanyanDB |
-| ops | meter_banyandb_term_search_rate | Term Search Rate (Operations per Second) | BanyanDB |
-| Count | meter_banyandb_total_document | Total Document Count | BanyanDB |
+
+1. Run a BanyanDB **0.11+** cluster (liaison + data nodes; data nodes may be tiered hot/warm/cold) with
+ the **FODC proxy** enabled and the Prometheus metrics provider on (default). Standalone mode is the
+ degenerate case — one cluster, one node, one `container_name=standalone`.
+2. Run an **OpenTelemetry Collector** whose `prometheus` receiver scrapes the FODC proxy `/metrics`
+ (`:17913`) as the single target and adds a static `cluster: <name>` label, exporting OTLP to OAP. For
+ a runnable example, see
+ [the e2e collector config](../../../test/e2e-v2/cases/banyandb/otel-collector-config.yaml).
+3. Enable SkyWalking's
+ [OpenTelemetry receiver](https://skywalking.apache.org/docs/main/next/en/setup/backend/opentelemetry-receiver/).
+ The `banyandb/*` rules are enabled by default in `enabledOtelMetricsRules`.
+4. Open the **Horizon UI** → `BanyanDB` layer.
+
+## Metrics
+
+The metric source expressions mirror the upstream BanyanDB Grafana boards, so the SkyWalking dashboards
+stay in lockstep with the BanyanDB catalog. The rule files are
+`otel-rules/banyandb/banyandb-service.yaml`, `banyandb-instance.yaml` and `banyandb-endpoint.yaml`.
+
+### Service scope — cluster summary (`meter_banyandb_*`)
+
+| Unit | Metric | Description |
+| ---- | ------ | ----------- |
+| w/s | `meter_banyandb_cluster_write_rate` | Cluster write rate across measure/stream/trace |
+| r/s | `meter_banyandb_cluster_query_rate` | Cluster query rate |
+| c/m | `meter_banyandb_cluster_error_rate` | Cluster error rate (counts/min) |
+| Count | `meter_banyandb_reporting_instances` | Live container count by role |
+| Count | `meter_banyandb_total_cpu_cores` | Cluster CPU capacity |
+| Bytes | `meter_banyandb_total_memory_used` | Cluster memory used |
+| Bytes | `meter_banyandb_total_disk_used` | Cluster disk used |
+
+### Instance scope — per container (`meter_banyandb_instance_*`)
+
+**All roles** (every container emits these):
+
+| Unit | Metric | Description |
+| ---- | ------ | ----------- |
+| s | `node_uptime` | Node uptime |
+| Cores | `cpu_usage` | CPU usage |
+| Bytes | `rss_memory` | Resident memory |
+| percentunit | `system_memory_percent` | System memory used fraction |
+| percentunit | `disk_usage_percent` | Disk used fraction (Σused/Σtotal) |
+| Bytes | `disk_used_by_path` / `disk_total_by_path` | Disk used / total by mount path |
+| percentunit | `disk_used_percent_by_path` | Disk used fraction by mount path |
+| Bytes/s | `network_recv` / `network_sent` | Network throughput by interface |
+| Count | `goroutines` | Go goroutines |
+| s | `gc_pause_avg` | Average GC pause |
+| Bytes | `heap_inuse` / `heap_next_gc` | Go heap in-use / next-GC threshold |
+| Bytes/s | `alloc_rate` | Go allocation rate |
+
+**Liaison** (front door; the dashboard gates these on `container_name == 'liaison'`):
+
+| Unit | Metric | Description |
+| ---- | ------ | ----------- |
+| r/s | `query_rate_by_service` | Query rate by data-model service |
+| c/m | `grpc_error_rate` | gRPC error rate |
+| r/s | `non_query_op_rate` | Registry / non-query operation rate |
+| w/s | `write_rate` | Write rate seen at the front door |
+| ops | `publish_throughput` | Tier-2 publish throughput by operation |
+| Bytes/s | `publish_bytes` | Publish bytes |
+| s | `publish_latency_p99` | Publish send latency p99 |
+| Count | `wqueue_pending` / `wqueue_file_parts` / `wqueue_mem_part` | Write-queue depth |
+
+**Data** (backend; the dashboard gates these on `container_name == 'data'`):
+
+| Unit | Metric | Description |
+| ---- | ------ | ----------- |
+| Count | `total_data` | Total stored data elements |
+| o/s | `merge_file_rate` | Merge-loop rate |
+| Count | `merge_file_partitions` | Avg parts merged per loop |
+| s | `merge_file_latency` | Avg file-merge latency |
+| o/s | `series_write_rate` / `series_term_search_rate` | Inverted-index write / term-search rate |
+| Count | `total_series` | Inverted-index documents |
+| o/s | `stream_tst_write_rate` / `stream_tst_term_search_rate` | Stream tst index write / term-search rate |
+| Count | `stream_tst_total_docs` | Stream tst index documents |
+| ops | `queue_sub_throughput` | Subscribe-queue throughput by operation |
+| s | `queue_sub_latency_p99` | Subscribe-queue latency p99 |
+| percent | `retention_measure_disk_usage_percent` / `retention_stream_disk_usage_percent` / `retention_trace_disk_usage_percent` | Retention disk-usage % per scope |
+
+**Lifecycle** (the tier-migration sidecar on hot/warm data pods; `container_name == 'lifecycle'`):
+
+| Unit | Metric | Description |
+| ---- | ------ | ----------- |
+| Count | `lifecycle_cycles` | Cumulative migration cycles |
+| s | `lifecycle_last_run` | Seconds since the last migration cycle started |
+| Status | `lifecycle_last_run_success` | Last cycle status (1 = OK, 0 = failed) |
+
+### Endpoint scope — per group (`meter_banyandb_endpoint_*`)
+
+| Unit | Metric | Description |
+| ---- | ------ | ----------- |
+| w/s | `write_rate` | Write rate for the group |
+| s | `query_latency` | Mean query latency for the group |
+| Count | `total_data` | Total stored data elements for the group |
+| o/s | `merge_file_rate` | Merge-loop rate for the group |
+| s | `merge_file_latency` | Avg file-merge latency for the group |
+| Count | `merge_file_partitions` | Avg parts merged per loop for the group |
+| o/s | `series_write_rate` | Inverted-index write rate for the group |
+| Count | `total_series` | Inverted-index documents for the group |
+| ops | `queue_throughput` | Subscribe-queue throughput by operation for the group |
+| s | `queue_latency_p99` | Publish-queue latency p99 for the group |
+| Bytes/s | `publish_bytes` | Publish bytes for the group |
## Customizations
-You can customize your own metrics/expression/dashboard panel.The metrics definition and expression rules are found in `/config/otel-rules/banyandb`.The [BanyanDB](https://skywalking.apache.org/docs/skywalking-banyandb/next/readme/) dashboard panel configurations ship from the SkyWalking Horizon UI bundle (apache/skywalking-horizon-ui); the OAP backend no longer hosts UI dashboard JSONs.
+
+You can customize your own metrics / expressions. The metric definitions and expression rules are in
+`/config/otel-rules/banyandb`. The dashboard panel configurations ship from the SkyWalking Horizon UI
+bundle (apache/skywalking-horizon-ui); the OAP backend does not host UI dashboard JSONs.
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index d548d25cd5..f6dd1e414a 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -242,6 +242,14 @@
admin-host only" entry above for the public REST retirement.
#### OAP Server
+* SWIP-15: rebuild BanyanDB self-observability around the cluster / container / group model
+ (requires BanyanDB 0.11+). `otel-rules/banyandb/` now models a BanyanDB cluster as one `Service`
+ (`service(['cluster'])`), each container as a `ServiceInstance` keyed on `pod_name` + `container_name`
+ (with `node_role` / `node_type` / `container_name` / `pod_name` as instance attributes), and each
+ storage group as an `Endpoint`. New `banyandb-endpoint.yaml`; `banyandb-service.yaml` and
+ `banyandb-instance.yaml` redesigned to mirror the upstream FODC-proxy Grafana boards. The stale
+ single-node `host_name` model and the removed `etcd_operation_rate` / `up`-derived `active_instance`
+ metrics are gone.
* Runtime MAL/LAL hot-update rules can declare `layerDefinitions:` to introduce new
layers. Ordinals are operator-pinned in the `100_000+` tier; the layer is
refcount-tracked and unregistered when the last declaring rule is removed. See
diff --git a/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-endpoint.yaml b/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-endpoint.yaml
new file mode 100644
index 0000000000..ef61c460c8
--- /dev/null
+++ b/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-endpoint.yaml
@@ -0,0 +1,96 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# SWIP-15 section 3.3 Endpoint scope: a BanyanDB `group` (storage group, e.g. sw_metricsMinute,
+# sw_trace) is modeled as an Endpoint under the cluster Service. The `cluster` label is the
+# single static label the OTel collector injects per scrape job (it is NOT on the raw FODC
+# wire); `group` is carried natively by every family referenced below. Every metric here is
+# aggregated across all cluster nodes per group, so each rule's .sum() collapses the per-node /
+# per-seg / per-shard / per-operation / per-remote dimensions down to ['cluster','group']
+# before any rate/histogram/division. MAL arithmetic ('+', '/') inner-joins on exact label
+# equality, so every operand is reduced to the identical ['cluster','group'] (or
+# ['cluster','group','le'] for histograms) label set first.
+# Source expressions mirror the upstream BanyanDB Grafana "Workload" board
+# (docs/operation/grafana-fodc-workload.json).
+filter: "{ tags -> tags.job_name == 'banyandb-monitoring' }"
+expSuffix: endpoint(['cluster'], ['group'], Layer.BANYANDB)
+metricPrefix: meter_banyandb_endpoint
+metricsRules:
+ # writes/s for the group, across the three data-model scopes (measure, stream, trace). The
+ # write counter carries `group` regardless of which role records it, so the by-group roll-up
+ # is exact.
+ - name: write_rate
+ exp: (banyandb_measure_total_written.sum(['cluster', 'group']).rate('PT1M') + banyandb_stream_tst_total_written.sum(['cluster', 'group']).rate('PT1M') + banyandb_trace_tst_total_written.sum(['cluster', 'group']).rate('PT1M'))
+
+ # mean query latency (ms) for the group = sum(latency) / sum(count). liaison_grpc_total_latency
+ # and _started are BOTH counters (not a histogram), so this is a ratio of cumulative counters,
+ # not a percentile. Both filtered to method='query' and reduced to ['cluster','group']
+ # (collapsing the `service` data-model facet) before the division joins on equal labels.
+ - name: query_latency
+ exp: (banyandb_liaison_grpc_total_latency.tagEqual('method', 'query').sum(['cluster', 'group']) / banyandb_liaison_grpc_total_started.tagEqual('method', 'query').sum(['cluster', 'group'])) * 1000
+
+ # current total stored data elements for the group (gauge). Dimensioned by seg+shard+node_type
+ # across data nodes; .sum(['cluster','group']) collapses them into one per-group total.
+ - name: total_data
+ exp: (banyandb_measure_total_file_elements.sum(['cluster', 'group']) + banyandb_stream_tst_total_file_elements.sum(['cluster', 'group']) + banyandb_trace_tst_total_file_elements.sum(['cluster', 'group']))
+
+ # merge-loop iterations/min for the group (matches the upstream "Merge File Rate" rotrpm panel,
+ # which is rate(merge_loop_started) * 60). merge_loop_started carries node_type (NOT a `type`
+ # label), so no type filter applies here.
+ - name: merge_file_rate
+ exp: (banyandb_measure_total_merge_loop_started.sum(['cluster', 'group']).rate('PT1M') + banyandb_stream_tst_total_merge_loop_started.sum(['cluster', 'group']).rate('PT1M') + banyandb_trace_tst_total_merge_loop_started.sum(['cluster', 'group']).rate('PT1M')) * 60
+
+ # mean file-merge latency (ms) per merge loop for the group. merge_latency carries a `type`
+ # label (file/hot/mem); type='file' selects on-disk merges and is DATA-only on the wire
+ # (liaison emits only type='mem'). Divide accumulated merge-seconds by merge loops, both
+ # type/scope-aligned to ['cluster','group']. Matches the upstream "Merge File Latency" panel.
+ - name: merge_file_latency
+ exp: ((banyandb_measure_total_merge_latency.tagEqual('type', 'file').sum(['cluster', 'group']).rate('PT1M') / banyandb_measure_total_merge_loop_started.sum(['cluster', 'group']).rate('PT1M')) + (banyandb_stream_tst_total_merge_latency.tagEqual('type', 'file').sum(['cluster', 'group']).rate('PT1M') / banyandb_stream_tst_total_merge_loop_started.sum(['cluster', 'group']).rate('PT1M')) + (banyandb_trace_tst_total_merge_latency.tagEqual('type', 'file').sum(['cluster', 'group']).rate('PT1 [...]
+
+ # avg parts merged per merge loop on the on-disk merge path for the group (matches the upstream
+ # "Merge File Partitions" panel = rate(merged_parts{type=file}) / rate(merge_loop_started)).
+ # merged_parts carries `type`; type='file' is DATA-only (liaison emits only type='mem').
+ - name: merge_file_partitions
+ exp: ((banyandb_measure_total_merged_parts.tagEqual('type', 'file').sum(['cluster', 'group']).rate('PT1M') / banyandb_measure_total_merge_loop_started.sum(['cluster', 'group']).rate('PT1M')) + (banyandb_stream_tst_total_merged_parts.tagEqual('type', 'file').sum(['cluster', 'group']).rate('PT1M') / banyandb_stream_tst_total_merge_loop_started.sum(['cluster', 'group']).rate('PT1M')) + (banyandb_trace_tst_total_merged_parts.tagEqual('type', 'file').sum(['cluster', 'group']).rate('PT1M') [...]
+
+ # inverted-index updates/s for the group. NOTE: *_inverted_index_total_updates is # TYPE=gauge
+ # though cumulative; rate() over a cumulative gauge yields a per-window delta (updates/s). Stream
+ # uses two index scopes -- both stream_storage_* and stream_tst_* are summed in. Data-only family.
+ - name: series_write_rate
+ exp: (banyandb_measure_inverted_index_total_updates.sum(['cluster', 'group']).rate('PT1M') + banyandb_stream_storage_inverted_index_total_updates.sum(['cluster', 'group']).rate('PT1M') + banyandb_stream_tst_inverted_index_total_updates.sum(['cluster', 'group']).rate('PT1M'))
+
+ # total inverted-index documents (series proxy) for the group (gauge, direct read, no rate).
+ # Both stream index scopes summed. Dimensioned by seg across data nodes; sum collapses to group.
+ - name: total_series
+ exp: (banyandb_measure_inverted_index_total_doc_count.sum(['cluster', 'group']) + banyandb_stream_storage_inverted_index_total_doc_count.sum(['cluster', 'group']) + banyandb_stream_tst_inverted_index_total_doc_count.sum(['cluster', 'group']))
+
+ # subscribe-side queue throughput (msgs/s) for the group, broken out by operation. queue_sub is
+ # emitted on BOTH data (operations batch-write/control/file-sync/query) and liaison
+ # (batch-write only); `operation` is kept in the group-by so the dashboard can split per op.
+ - name: queue_throughput
+ exp: banyandb_queue_sub_total_finished.sum(['cluster', 'group', 'operation']).rate('PT1M')
+
+ # publish-side queue p99 latency for the group. queue_pub_total_latency IS a histogram on the
+ # wire (_bucket carries le); keep le + group + operation in the .sum() group-by, then
+ # .histogram().histogram_percentile([99]). queue_pub is liaison-only. Precedent:
+ # oap.yaml / nginx-endpoint.yaml histogram idiom.
+ - name: queue_latency_p99
+ exp: banyandb_queue_pub_total_latency.sum(['le', 'cluster', 'group', 'operation']).histogram().histogram_percentile([99])
+
+ # publish bytes/s for the group. Wire family is banyandb_queue_pub_sent_bytes -- NO `total`
+ # infix (unlike queue_pub_total_started/_finished). Liaison-only; sum collapses
+ # operation/remote_node/remote_role/remote_tier before the rate.
+ - name: publish_bytes
+ exp: banyandb_queue_pub_sent_bytes.sum(['cluster', 'group']).rate('PT1M')
diff --git a/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-instance.yaml b/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-instance.yaml
index 21955331f3..c3f728b139 100644
--- a/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-instance.yaml
+++ b/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-instance.yaml
@@ -13,74 +13,157 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# This will parse a textual representation of a duration. The formats
-# accepted are based on the ISO-8601 duration format {@code PnDTnHnMn.nS}
-# with days considered to be exactly 24 hours.
-# <p>
-# Examples:
-# <pre>
-# "PT20.345S" -- parses as "20.345 seconds"
-# "PT15M" -- parses as "15 minutes" (where a minute is 60 seconds)
-# "PT10H" -- parses as "10 hours" (where an hour is 3600 seconds)
-# "P2D" -- parses as "2 days" (where a day is 24 hours or 86400 seconds)
-# "P2DT3H4M" -- parses as "2 days, 3 hours and 4 minutes"
-# "P-6H3M" -- parses as "-6 hours and +3 minutes"
-# "-P6H3M" -- parses as "-6 hours and -3 minutes"
-# "-P-6H+3M" -- parses as "+6 hours and -3 minutes"
-# </pre>
+# SWIP-15: BanyanDB self-observability, ServiceInstance scope = one container on a node.
+# The instance identity is pod_name + container_name (a data hot/warm pod co-hosts a data and a
+# lifecycle container under one pod_name), joined by '@'. role (container_name) and tier (node_type)
+# ride as instance attributes via the 6-arg instance() properties closure; node_type Elvis-defaults
+# to 'n/a' off data containers (it is absent on liaison samples, present on every ROLE_DATA sample).
+#
+# Every rule that aggregates keeps ['cluster','pod_name','container_name','node_role','node_type'] in
+# its .sum()/.avg()/.max() group-by: SampleFamily.aggregate() drops labels not in the group-by, and
+# the properties closure reads them from the post-aggregation sample (SampleFamily.java:810). node_type
+# rides on every ROLE_DATA sample (system_*, go_*, process_* included), so a data instance resolves a
+# stable tier across all rules; liaison families carry none, so liaison resolves 'n/a' consistently.
+#
+# Source expressions mirror the upstream BanyanDB Grafana "Nodes" board
+# (docs/operation/grafana-fodc-nodes.json) plus the liaison/data rows of the "Workload" board, so the
+# SkyWalking instance dashboard stays in lockstep with the upstream catalog.
filter: "{ tags -> tags.job_name == 'banyandb-monitoring' }"
-expSuffix: tag({tags -> tags.host_name = 'banyandb::' + tags.host_name}).service(['host_name'] , Layer.BANYANDB).instance(['host_name'], ['service_instance_id'], Layer.BANYANDB)
-metricPrefix: meter_banyandb
+expSuffix: |-
+ service(['cluster'], Layer.BANYANDB)
+ .instance(['cluster'], '::', ['pod_name', 'container_name'], '@', Layer.BANYANDB, { tags -> ['node_role': tags.node_role, 'node_type': tags.node_type ?: 'n/a', 'pod_name': tags.pod_name, 'container_name': tags.container_name] })
+metricPrefix: meter_banyandb_instance
metricsRules:
- - name: instance_write_rate
- exp: banyandb_measure_total_written.rate('PT15S')+banyandb_stream_tst_total_written.rate('PT15S')
- - name: instance_total_memory
- exp: banyandb_system_memory_state.tagEqual('kind','total')
- - name: instance_disk_usage
- exp: banyandb_system_disk.tagEqual('kind','used').sum(['host_name','service_instance_id'])
- - name: instance_query_rate
- exp: banyandb_liaison_grpc_total_started.sum(['method','host_name','service_instance_id'])
- - name: instance_total_cpu
- exp: banyandb_system_cpu_num
- - name: instance_write_and_query_errors_rate
- exp: banyandb_liaison_grpc_total_err.tagEqual('method','query').sum(['method','host_name','service_instance_id']).rate('PT15S')*60 + banyandb_liaison_grpc_total_stream_msg_sent_err.sum(['host_name','service_instance_id']).rate('PT15S')*60 + banyandb_liaison_grpc_total_stream_msg_received_err.sum(['host_name','service_instance_id']).rate('PT15S')*60 + banyandb_queue_sub_total_msg_sent_err.sum(['host_name','service_instance_id']).rate('PT15S')*60
- - name: instance_etcd_operation_rate
- exp: banyandb_liaison_grpc_total_registry_started.sum(['host_name','service_instance_id']).rate('PT15S') + banyandb_liaison_grpc_total_started.sum(['host_name','service_instance_id']).rate('PT15S')
- - name: instance_active_instance
- exp: up.sum(['host_name','service_instance_id']).downsampling(MIN)
- - name: instance_cpu_usage
- exp: (((process_cpu_seconds_total.sum(['host_name','service_instance_id']).rate('PT15S') / banyandb_system_cpu_num.sum(['host_name','service_instance_id']))).max(['host_name','service_instance_id']))*1000
- - name: instance_rss_memory_usage
- exp: ((process_resident_memory_bytes.sum(['host_name','service_instance_id']).downsampling(MAX) / banyandb_system_memory_state.tagEqual('kind','total').sum(['host_name','service_instance_id'])).max(['host_name','service_instance_id']))*1000
- - name: instance_disk_usage_all
- exp: ((banyandb_system_disk.tagEqual('kind','used').sum(['host_name','service_instance_id']) / banyandb_system_memory_state.tagEqual('kind','total').sum(['host_name','service_instance_id'])).max(['host_name','service_instance_id']))*1000
- - name: instance_network_usage_recv
- exp: banyandb_system_net_state.tagEqual('kind','bytes_recv').sum(['host_name','service_instance_id']).rate('PT15S')
- - name: instance_network_usage_sent
- exp: banyandb_system_net_state.tagEqual('kind','bytes_sent').sum(['host_name','service_instance_id']).rate('PT15S')
- - name: instance_storage_write_rate
- exp: banyandb_measure_total_written.sum(['group','host_name','service_instance_id']).rate('PT15S')*1000
- - name: instance_query_latency
- exp: (banyandb_liaison_grpc_total_latency.tagEqual('method','query').sum(['group','host_name','service_instance_id']).rate('PT15S') / banyandb_liaison_grpc_total_started.tagEqual('method','query').sum(['group','host_name','service_instance_id']).rate('PT15S'))*1000
- - name: instance_total_data
- exp: banyandb_measure_total_file_elements.sum(['group','host_name','service_instance_id'])
- - name: instance_merge_file_data
- exp: banyandb_measure_total_merge_loop_started.sum(['group','host_name','service_instance_id']).rate('PT15S') * 60 *1000
- - name: instance_merge_file_latency
- exp: (banyandb_measure_total_merge_latency.tagEqual('type','file').sum(['group','host_name','service_instance_id']).rate('PT15S') / banyandb_measure_total_merge_loop_started.sum(['group','host_name','service_instance_id']).rate('PT15S'))*1000
- - name: instance_merge_file_partitions
- exp: (banyandb_measure_total_merged_parts.tagEqual('type','file').sum(['group','host_name','service_instance_id']).rate('PT15S') / banyandb_measure_total_merge_loop_started.sum(['group','host_name','service_instance_id']).rate('PT15S'))*1000
- - name: instance_series_write_rate
- exp: (banyandb_measure_inverted_index_total_updates.sum(['group','host_name','service_instance_id']).rate('PT15S'))*1000
- - name: instance_series_term_search_rate
- exp: banyandb_stream_storage_inverted_index_total_term_searchers_started.sum(['group','host_name','service_instance_id']).rate('PT15S')
- - name: instance_total_series
- exp: banyandb_measure_inverted_index_total_doc_count.sum(['group','host_name','service_instance_id'])
- - name: instance_stream_write_rate
- exp: banyandb_stream_tst_inverted_index_total_updates.sum(['group','host_name','service_instance_id']).rate('PT15S')
- - name: instance_term_search_rate
- exp: banyandb_stream_tst_inverted_index_total_term_searchers_started.sum(['group','host_name','service_instance_id']).rate('PT15S')* 1000
- - name: instance_total_document
- exp: banyandb_stream_tst_inverted_index_total_doc_count.sum(['group','host_name','service_instance_id'])
+ # ---- All roles: Resources / Disk by Path / Go Runtime (every container emits these) ----
+ # node uptime (s). Raw gauge; ABSENT on lifecycle containers (their binary runs the metric service
+ # without the system collector), so the lifecycle instance shows no uptime.
+ - name: node_uptime
+ exp: banyandb_system_up_time
+ # CPU usage (cores). process_* rides on every container including lifecycle.
+ - name: cpu_usage
+ exp: process_cpu_seconds_total.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+ # resident memory (bytes). Raw gauge, present on all containers.
+ - name: rss_memory
+ exp: process_resident_memory_bytes
+ # system memory used %. kind='used_percent' is emitted directly (a 0-1 fraction; source divides by 100).
+ - name: system_memory_percent
+ exp: banyandb_system_memory_state.tagEqual('kind','used_percent')
+ # disk used % = Σused / Σtotal across the node's data paths (matches the Grafana "Disk Usage %" panel).
+ - name: disk_usage_percent
+ exp: banyandb_system_disk.tagEqual('kind','used').sum(['cluster','pod_name','container_name','node_role','node_type']) / banyandb_system_disk.tagEqual('kind','total').sum(['cluster','pod_name','container_name','node_role','node_type'])
+ # disk used / total / used% broken out per mount path.
+ - name: disk_used_by_path
+ exp: banyandb_system_disk.tagEqual('kind','used').sum(['cluster','pod_name','container_name','node_role','node_type','path'])
+ - name: disk_total_by_path
+ exp: banyandb_system_disk.tagEqual('kind','total').sum(['cluster','pod_name','container_name','node_role','node_type','path'])
+ - name: disk_used_percent_by_path
+ exp: banyandb_system_disk.tagEqual('kind','used').sum(['cluster','pod_name','container_name','node_role','node_type','path']) / banyandb_system_disk.tagEqual('kind','total').sum(['cluster','pod_name','container_name','node_role','node_type','path'])
+ # network throughput (bytes/s) by interface name.
+ - name: network_recv
+ exp: banyandb_system_net_state.tagEqual('kind','bytes_recv').sum(['cluster','pod_name','container_name','node_role','node_type','name']).rate('PT15S')
+ - name: network_sent
+ exp: banyandb_system_net_state.tagEqual('kind','bytes_sent').sum(['cluster','pod_name','container_name','node_role','node_type','name']).rate('PT15S')
+ # Go runtime.
+ - name: goroutines
+ exp: go_goroutines
+ # average GC pause (s) = rate(Σpause) / rate(Σcount). go_gc_duration_seconds is a summary (no buckets),
+ # so this ratio of _sum/_count is the only valid average — do not apply histogram_percentile to it.
+ - name: gc_pause_avg
+ exp: go_gc_duration_seconds_sum.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') / go_gc_duration_seconds_count.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+ - name: heap_inuse
+ exp: go_memstats_heap_inuse_bytes
+ - name: heap_next_gc
+ exp: go_memstats_next_gc_bytes
+ - name: alloc_rate
+ exp: go_memstats_alloc_bytes_total.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+
+ # ---- Liaison only (front door; the dashboard gates these on container_name == liaison) ----
+ # query rate (req/s) by data-model service (measure/stream/trace/property). method literal is "query".
+ - name: query_rate_by_service
+ exp: banyandb_liaison_grpc_total_started.tagEqual('method','query').sum(['cluster','pod_name','container_name','node_role','node_type','service']).rate('PT15S')
+ # gRPC errors/min. Three liaison-side error families (mirrors the Grafana "gRPC Error Rate" panel,
+ # which sums total_err + registry_err + stream_msg_received_err). All lazily registered -> empty on a
+ # healthy cluster; each pre-aggregated to the same label set before '+'.
+ - name: grpc_error_rate
+ exp: (banyandb_liaison_grpc_total_err.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') + banyandb_liaison_grpc_total_registry_err.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') + banyandb_liaison_grpc_total_stream_msg_received_err.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')) * 60
+ # non-query operation rate (req/s): registry ops + any non-query unary call. total_started is
+ # query-only on the wire, so tagNotEqual('method','query') is empty today; registry_started carries it.
+ - name: non_query_op_rate
+ exp: banyandb_liaison_grpc_total_registry_started.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') + banyandb_liaison_grpc_total_started.tagNotEqual('method','query').sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+ # write rate (writes/s) seen at the liaison front door. group label dropped (instance-level total).
+ - name: write_rate
+ exp: banyandb_measure_total_written.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') + banyandb_stream_tst_total_written.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') + banyandb_trace_tst_total_written.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+ # tier-2 publish pipeline (liaison -> data): throughput by operation, bytes/s, and p99 send latency.
+ - name: publish_throughput
+ exp: banyandb_queue_pub_total_finished.sum(['cluster','pod_name','container_name','node_role','node_type','operation']).rate('PT15S')
+ - name: publish_bytes
+ exp: banyandb_queue_pub_sent_bytes.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+ - name: publish_latency_p99
+ exp: banyandb_queue_pub_total_latency.sum(['cluster','pod_name','container_name','node_role','node_type','operation','le']).histogram().histogram_percentile([99])
+ # write-queue (wqueue) depth: pending records, on-disk file parts, in-memory parts. On the liaison
+ # these reflect the write buffer; the same families on data containers reflect storage parts (the
+ # dashboard gates on container_name). Gauges, summed to the instance.
+ - name: wqueue_pending
+ exp: banyandb_measure_pending_data_count.sum(['cluster','pod_name','container_name','node_role','node_type']) + banyandb_stream_tst_pending_data_count.sum(['cluster','pod_name','container_name','node_role','node_type']) + banyandb_trace_tst_pending_data_count.sum(['cluster','pod_name','container_name','node_role','node_type'])
+ - name: wqueue_file_parts
+ exp: banyandb_measure_total_file_parts.sum(['cluster','pod_name','container_name','node_role','node_type']) + banyandb_stream_tst_total_file_parts.sum(['cluster','pod_name','container_name','node_role','node_type']) + banyandb_trace_tst_total_file_parts.sum(['cluster','pod_name','container_name','node_role','node_type'])
+ - name: wqueue_mem_part
+ exp: banyandb_measure_total_mem_part.sum(['cluster','pod_name','container_name','node_role','node_type']) + banyandb_stream_tst_total_mem_part.sum(['cluster','pod_name','container_name','node_role','node_type']) + banyandb_trace_tst_total_mem_part.sum(['cluster','pod_name','container_name','node_role','node_type'])
+ # ---- Data only (backend; the dashboard gates these on container_name == data) ----
+ # total stored data elements (gauge).
+ - name: total_data
+ exp: banyandb_measure_total_file_elements.sum(['cluster','pod_name','container_name','node_role','node_type']) + banyandb_stream_tst_total_file_elements.sum(['cluster','pod_name','container_name','node_role','node_type']) + banyandb_trace_tst_total_file_elements.sum(['cluster','pod_name','container_name','node_role','node_type'])
+ # merge-loop iterations/s.
+ - name: merge_file_rate
+ exp: banyandb_measure_total_merge_loop_started.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') + banyandb_stream_tst_total_merge_loop_started.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') + banyandb_trace_tst_total_merge_loop_started.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+ # avg parts merged per merge loop on the file path (matches Grafana = rate(merged_parts{type=file}) /
+ # rate(merge_loop_started)). type='file' is data-only on the wire (liaison emits only type='mem').
+ - name: merge_file_partitions
+ exp: (banyandb_measure_total_merged_parts.tagEqual('type','file').sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') / banyandb_measure_total_merge_loop_started.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')) + (banyandb_stream_tst_total_merged_parts.tagEqual('type','file').sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') / banyandb_stream_tst_total_merge_loop_started.sum(['cluster', [...]
+ # avg file-merge latency (ms) per merge loop.
+ - name: merge_file_latency
+ exp: ((banyandb_measure_total_merge_latency.tagEqual('type','file').sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') / banyandb_measure_total_merge_loop_started.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')) + (banyandb_stream_tst_total_merge_latency.tagEqual('type','file').sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') / banyandb_stream_tst_total_merge_loop_started.sum(['cluste [...]
+ # inverted-index (series) write rate / term-search rate / total docs. *_inverted_index_total_* are
+ # # TYPE=gauge but cumulative, so rate() yields a per-window delta. Stream's series index is the
+ # storage scope (stream_storage_*); the tst scope is reported separately below.
+ - name: series_write_rate
+ exp: banyandb_measure_inverted_index_total_updates.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') + banyandb_stream_storage_inverted_index_total_updates.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+ - name: series_term_search_rate
+ exp: banyandb_measure_inverted_index_total_term_searchers_started.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S') + banyandb_stream_storage_inverted_index_total_term_searchers_started.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+ - name: total_series
+ exp: banyandb_measure_inverted_index_total_doc_count.sum(['cluster','pod_name','container_name','node_role','node_type']) + banyandb_stream_storage_inverted_index_total_doc_count.sum(['cluster','pod_name','container_name','node_role','node_type'])
+ # stream time-series-table (tst) index, distinct from the stream series (storage) index above.
+ - name: stream_tst_write_rate
+ exp: banyandb_stream_tst_inverted_index_total_updates.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+ - name: stream_tst_term_search_rate
+ exp: banyandb_stream_tst_inverted_index_total_term_searchers_started.sum(['cluster','pod_name','container_name','node_role','node_type']).rate('PT15S')
+ - name: stream_tst_total_docs
+ exp: banyandb_stream_tst_inverted_index_total_doc_count.sum(['cluster','pod_name','container_name','node_role','node_type'])
+ # subscribe-side queue (data receives from liaison): throughput by operation + p99 latency.
+ - name: queue_sub_throughput
+ exp: banyandb_queue_sub_total_finished.sum(['cluster','pod_name','container_name','node_role','node_type','operation']).rate('PT15S')
+ - name: queue_sub_latency_p99
+ exp: banyandb_queue_sub_total_latency.sum(['cluster','pod_name','container_name','node_role','node_type','operation','le']).histogram().histogram_percentile([99])
+ # retention disk-usage % per data-model scope (0-100 gauge). Kept per scope rather than summed (a sum
+ # of three percentages is meaningless). Not in the upstream Grafana boards; a SkyWalking addition.
+ - name: retention_measure_disk_usage_percent
+ exp: banyandb_storage_retention_measure_disk_usage_percent
+ - name: retention_stream_disk_usage_percent
+ exp: banyandb_storage_retention_stream_disk_usage_percent
+ - name: retention_trace_disk_usage_percent
+ exp: banyandb_storage_retention_trace_disk_usage_percent
+ # ---- Lifecycle only (the tier-migration sidecar on hot/warm data pods; container_name == lifecycle) ----
+ # cumulative migration cycles. Compiled into build #1166 (BanyanDB #1164) but lazily registered:
+ # emits no series until the first migration cycle fires. Dashboard renders absent-as-0.
+ - name: lifecycle_cycles
+ exp: banyandb_lifecycle_cycles_total
+ # seconds since the last migration cycle started = now - epoch. time() is the MAL ingest-time scalar
+ # (MQE has no current-time function), computed when the rule runs. BUILD-GATED: the source gauge is
+ # BanyanDB #1167+, absent on build #1166 -> no series until the cluster runs >= #1167 AND a cycle ends.
+ - name: lifecycle_last_run
+ exp: (time() - banyandb_lifecycle_last_run_timestamp_seconds.max(['cluster','pod_name','container_name','node_role','node_type']))
+ # last cycle status (1 = OK, 0 = failed). Same #1167 build gate as lifecycle_last_run.
+ - name: lifecycle_last_run_success
+ exp: banyandb_lifecycle_last_run_success
diff --git a/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-service.yaml b/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-service.yaml
index 566f893cc4..97c6cac8f6 100644
--- a/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-service.yaml
+++ b/oap-server/server-starter/src/main/resources/otel-rules/banyandb/banyandb-service.yaml
@@ -13,74 +13,51 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# This will parse a textual representation of a duration. The formats
-# accepted are based on the ISO-8601 duration format {@code PnDTnHnMn.nS}
-# with days considered to be exactly 24 hours.
-# <p>
-# Examples:
-# <pre>
-# "PT20.345S" -- parses as "20.345 seconds"
-# "PT15M" -- parses as "15 minutes" (where a minute is 60 seconds)
-# "PT10H" -- parses as "10 hours" (where an hour is 3600 seconds)
-# "P2D" -- parses as "2 days" (where a day is 24 hours or 86400 seconds)
-# "P2DT3H4M" -- parses as "2 days, 3 hours and 4 minutes"
-# "P-6H3M" -- parses as "-6 hours and +3 minutes"
-# "-P6H3M" -- parses as "-6 hours and -3 minutes"
-# "-P-6H+3M" -- parses as "+6 hours and -3 minutes"
-# </pre>
+# SWIP-15: BanyanDB self-observability, Service scope = one BanyanDB cluster.
+# The FODC proxy is the single scrape target; the collector injects one static label
+# `cluster` (the only label SkyWalking must add). Every BanyanDB-native family carries
+# the `banyandb_` prefix; only the Go-runtime / process exporter families (go_* / process_*)
+# are bare. All cluster KPIs collapse to the single `cluster` series via `.sum(['cluster'])`,
+# which is also what makes the heterogeneous error families joinable by MAL `+`
+# (MAL arithmetic inner-joins on exact label equality).
+# Source expressions mirror the upstream BanyanDB Grafana boards
+# (docs/operation/grafana-fodc-workload.json) so the SkyWalking dashboards stay in lockstep.
filter: "{ tags -> tags.job_name == 'banyandb-monitoring' }"
-expSuffix: tag({tags -> tags.host_name = 'banyandb::' + tags.host_name}).service(['host_name'] , Layer.BANYANDB)
+expSuffix: service(['cluster'], Layer.BANYANDB)
metricPrefix: meter_banyandb
metricsRules:
- - name: write_rate
- exp: (banyandb_measure_total_written.sum(['host_name','service_instance_id']).rate('PT15S') + banyandb_stream_tst_total_written.sum(['host_name','service_instance_id']).rate('PT15S'))
- - name: total_memory
- exp: banyandb_system_memory_state.tagEqual('kind','total').sum(['host_name'])
- - name: disk_usage
- exp: banyandb_system_disk.tagEqual('kind','used').sum(['host_name','service_instance_id'])
- - name: query_rate
- exp: banyandb_liaison_grpc_total_started.sum(['method','host_name','service_instance_id'])
- - name: total_cpu
- exp: banyandb_system_cpu_num.sum(['method','host_name','service_instance_id'])
- - name: write_and_query_errors_rate
- exp: banyandb_liaison_grpc_total_err.tagEqual('method','query').sum(['method','host_name','service_instance_id']).rate('PT15S')*60 + banyandb_liaison_grpc_total_stream_msg_sent_err.sum(['host_name','service_instance_id']).rate('PT15S')*60 + banyandb_liaison_grpc_total_stream_msg_received_err.sum(['host_name','service_instance_id']).rate('PT15S')*60 + banyandb_queue_sub_total_msg_sent_err.sum(['host_name','service_instance_id']).rate('PT15S')*60
- - name: etcd_operation_rate
- exp: banyandb_liaison_grpc_total_registry_started.sum(['host_name','service_instance_id']).rate('PT15S') + banyandb_liaison_grpc_total_started.sum(['host_name','service_instance_id']).rate('PT15S')
- - name: active_instance
- exp: up.sum(['host_name','service_instance_id']).downsampling(MIN)
- - name: cpu_usage
- exp: (((process_cpu_seconds_total.sum(['host_name','service_instance_id']).rate('PT15S') / banyandb_system_cpu_num.sum(['host_name','service_instance_id']))).max(['host_name','service_instance_id']))*1000
- - name: rss_memory_usage
- exp: ((process_resident_memory_bytes.sum(['host_name','service_instance_id']).downsampling(MAX) / banyandb_system_memory_state.tagEqual('kind','total').sum(['host_name','service_instance_id'])).max(['host_name','service_instance_id']))*1000
- - name: disk_usage_all
- exp: ((banyandb_system_disk.tagEqual('kind','used').sum(['host_name','service_instance_id']) / banyandb_system_memory_state.tagEqual('kind','total').sum(['host_name','service_instance_id'])).max(['host_name','service_instance_id']))*1000
- - name: network_usage_recv
- exp: banyandb_system_net_state.tagEqual('kind','bytes_recv').sum(['host_name','service_instance_id']).rate('PT15S')
- - name: network_usage_sent
- exp: banyandb_system_net_state.tagEqual('kind','bytes_sent').sum(['host_name','service_instance_id']).rate('PT15S')
- - name: storage_write_rate
- exp: banyandb_measure_total_written.sum(['group','host_name','service_instance_id']).rate('PT15S')*1000
- - name: query_latency
- exp: (banyandb_liaison_grpc_total_latency.tagEqual('method','query').sum(['group','host_name','service_instance_id']).rate('PT15S') / banyandb_liaison_grpc_total_started.tagEqual('method','query').sum(['group','host_name','service_instance_id']).rate('PT15S'))*1000
- - name: total_data
- exp: banyandb_measure_total_file_elements.sum(['group','host_name','service_instance_id'])
- - name: merge_file_data
- exp: banyandb_measure_total_merge_loop_started.sum(['group','host_name','service_instance_id']).rate('PT15S') * 60 *1000
- - name: merge_file_latency
- exp: (banyandb_measure_total_merge_latency.tagEqual('type','file').sum(['group','host_name','service_instance_id']).rate('PT15S') / banyandb_measure_total_merge_loop_started.sum(['group','host_name','service_instance_id']).rate('PT15S'))*1000
- - name: merge_file_partitions
- exp: (banyandb_measure_total_merged_parts.tagEqual('type','file').sum(['group','host_name','service_instance_id']).rate('PT15S') / banyandb_measure_total_merge_loop_started.sum(['group','host_name','service_instance_id']).rate('PT15S'))*1000
- - name: series_write_rate
- exp: (banyandb_measure_inverted_index_total_updates.sum(['group','host_name','service_instance_id']).rate('PT15S'))*1000
- - name: series_term_search_rate
- exp: banyandb_stream_storage_inverted_index_total_term_searchers_started.sum(['group','host_name','service_instance_id']).rate('PT15S')
- - name: total_series
- exp: banyandb_measure_inverted_index_total_doc_count.sum(['group','host_name','service_instance_id'])
- - name: stream_write_rate
- exp: banyandb_stream_tst_inverted_index_total_updates.sum(['group','host_name','service_instance_id']).rate('PT15S')
- - name: term_search_rate
- exp: banyandb_stream_tst_inverted_index_total_term_searchers_started.sum(['group','host_name','service_instance_id']).rate('PT15S')* 1000
- - name: total_document
- exp: banyandb_stream_tst_inverted_index_total_doc_count.sum(['group','host_name','service_instance_id'])
-
-
+ # cluster writes/s across the three data-model scopes (measure, stream, trace). Each scope's
+ # write counter is collapsed to one per-cluster series before `+`.
+ - name: cluster_write_rate
+ exp: (banyandb_measure_total_written.sum(['cluster']).rate('PT15S') + banyandb_stream_tst_total_written.sum(['cluster']).rate('PT15S') + banyandb_trace_tst_total_written.sum(['cluster']).rate('PT15S'))
+ # cluster queries/s. `service` on this family is BanyanDB's data-model facet
+ # (measure/stream/trace/property), not a SkyWalking service; method literal is "query".
+ - name: cluster_query_rate
+ exp: banyandb_liaison_grpc_total_started.tagEqual('method','query').sum(['cluster']).rate('PT15S')
+ # cluster errors/min. The seven liaison-side error families mirror the upstream Grafana
+ # "Error Rate" stat (grafana-fodc-workload.json). Each is pre-aggregated to ['cluster']
+ # BEFORE `+` because their wire label sets differ (stream_msg_received_err carries
+ # group/method/service, registry_err carries method/service, sync_loop_err carries group)
+ # and MAL `+` joins on exact label equality. On a healthy cluster most of these are lazily
+ # registered and emit no series; MAL treats an empty operand as the additive identity, so the
+ # sum emits from whatever has fired and renders absent-as-0 when nothing has.
+ - name: cluster_error_rate
+ exp: (banyandb_liaison_grpc_total_err.sum(['cluster']).rate('PT15S') + banyandb_liaison_grpc_total_registry_err.sum(['cluster']).rate('PT15S') + banyandb_liaison_grpc_total_stream_msg_received_err.sum(['cluster']).rate('PT15S') + banyandb_queue_pub_total_err.sum(['cluster']).rate('PT15S') + banyandb_measure_total_sync_loop_err.sum(['cluster']).rate('PT15S') + banyandb_stream_tst_total_sync_loop_err.sum(['cluster']).rate('PT15S') + banyandb_trace_tst_total_sync_loop_err.sum(['cluster' [...]
+ # live container count by role. count(['cluster','container_name','pod_name']) groups by all
+ # three then re-groups excluding the last key (pod_name), yielding one sample per
+ # (cluster, container_name) whose value = distinct pod_name count -> data=N, liaison=M.
+ # Mirrors the upstream "Nodes by Role" stat (count(banyandb_system_up_time) by container_name).
+ # CAVEAT: banyandb_system_up_time has NO lifecycle series (the lifecycle sidecar runs its
+ # metric service without the system collector), so this never emits a lifecycle row.
+ - name: reporting_instances
+ exp: banyandb_system_up_time.count(['cluster','container_name','pod_name'])
+ # cluster CPU capacity = sum of per-container visible core counts (no lifecycle series).
+ - name: total_cpu_cores
+ exp: banyandb_system_cpu_num.sum(['cluster'])
+ # cluster memory used (bytes). kind='used' is a real wire value (kind in total/used/used_percent).
+ - name: total_memory_used
+ exp: banyandb_system_memory_state.tagEqual('kind','used').sum(['cluster'])
+ # cluster disk used (bytes). system_disk carries a `path` label with multiple data roots;
+ # .sum(['cluster']) collapses all paths into one cluster total.
+ - name: total_disk_used
+ exp: banyandb_system_disk.tagEqual('kind','used').sum(['cluster'])
diff --git a/test/e2e-v2/cases/banyandb/banyandb-cases.yaml b/test/e2e-v2/cases/banyandb/banyandb-cases.yaml
index dfc901f490..83367a20c9 100644
--- a/test/e2e-v2/cases/banyandb/banyandb-cases.yaml
+++ b/test/e2e-v2/cases/banyandb/banyandb-cases.yaml
@@ -13,30 +13,59 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# This file contains BanyanDB instance metrics queries, referencing
-# oap-server/server-starter/src/main/resources/otel-rules/banyandb.yaml
-
+# SWIP-15 BanyanDB self-observability metrics, the cluster / container / group model. References
+# oap-server/server-starter/src/main/resources/otel-rules/banyandb/{banyandb-service,banyandb-instance,banyandb-endpoint}.yaml
+# Entity identities come from the collector's injected labels (otel-collector-config.yaml):
+# Service = cluster -> e2e-banyandb
+# Instance = pod_name '@' container_name -> banyandb-liaison-0@liaison, banyandb-data-hot-0@data
+# Endpoint = group -> sw_metricsMinute (an OAP self-telemetry group)
+# Expected templates:
+# metrics-has-value.yml — single unlabeled series (service/endpoint metrics summed to the entity key)
+# metrics-has-label-value.yml — labeled series. Instance metrics retain node_role/node_type labels
+# (kept in the .sum() group-by so the instance properties closure resolves
+# role/tier); reporting_instances is labeled by container_name; queue
+# metrics are labeled by operation.
+# This minimal cluster (1 liaison + 1 hot data, no FODC) intentionally does NOT cover: lifecycle
+# (no migration sidecar / cycle), warm & cold tiers, error counters (lazily registered -> empty on a
+# healthy cluster), or multi-node aggregation.
cases:
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_total_memory --service-name=banyandb::server
- expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_write_rate --service-name=banyandb::server --instance-name=banyandb:2121
- expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_total_memory --service-name=banyandb::server --instance-name=banyandb:2121
+ # ---- Service scope (cluster KPIs) ----
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_cluster_write_rate --service-name=e2e-banyandb
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_total_cpu --service-name=banyandb::server --instance-name=banyandb:2121
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_cluster_query_rate --service-name=e2e-banyandb
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_etcd_operation_rate --service-name=banyandb::server --instance-name=banyandb:2121
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_total_cpu_cores --service-name=e2e-banyandb
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_active_instance --service-name=banyandb::server --instance-name=banyandb:2121
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_total_memory_used --service-name=e2e-banyandb
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_cpu_usage --service-name=banyandb::server --instance-name=banyandb:2121
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_total_disk_used --service-name=e2e-banyandb
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_rss_memory_usage --service-name=banyandb::server --instance-name=banyandb:2121
+ # live container count by role (labeled by container_name)
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_reporting_instances --service-name=e2e-banyandb
+ expected: expected/metrics-has-label-value.yml
+
+ # ---- Instance scope (labeled by node_role/node_type, and operation where applicable) ----
+ # data node (banyandb-data-hot-0@data)
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_node_uptime --service-name=e2e-banyandb --instance-name=banyandb-data-hot-0@data
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_disk_usage_all --service-name=banyandb::server --instance-name=banyandb:2121
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_cpu_usage --service-name=e2e-banyandb --instance-name=banyandb-data-hot-0@data
+ expected: expected/metrics-has-label-value.yml
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_total_data --service-name=e2e-banyandb --instance-name=banyandb-data-hot-0@data
+ expected: expected/metrics-has-label-value.yml
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_queue_sub_throughput --service-name=e2e-banyandb --instance-name=banyandb-data-hot-0@data
+ expected: expected/metrics-has-label-value.yml
+ # liaison node (banyandb-liaison-0@liaison)
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_node_uptime --service-name=e2e-banyandb --instance-name=banyandb-liaison-0@liaison
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_network_usage_recv --service-name=banyandb::server --instance-name=banyandb:2121
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_write_rate --service-name=e2e-banyandb --instance-name=banyandb-liaison-0@liaison
+ expected: expected/metrics-has-label-value.yml
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_publish_throughput --service-name=e2e-banyandb --instance-name=banyandb-liaison-0@liaison
+ expected: expected/metrics-has-label-value.yml
+
+ # ---- Endpoint scope (storage group sw_metricsMinute) ----
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_endpoint_write_rate --service-name=e2e-banyandb --endpoint-name=sw_metricsMinute
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_instance_network_usage_sent --service-name=banyandb::server --instance-name=banyandb:2121
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_endpoint_total_data --service-name=e2e-banyandb --endpoint-name=sw_metricsMinute
expected: expected/metrics-has-value.yml
-
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=meter_banyandb_endpoint_queue_throughput --service-name=e2e-banyandb --endpoint-name=sw_metricsMinute
+ expected: expected/metrics-has-label-value.yml
diff --git a/test/e2e-v2/cases/banyandb/docker-compose.yml b/test/e2e-v2/cases/banyandb/docker-compose.yml
index 2b0d618a81..fd8468552b 100644
--- a/test/e2e-v2/cases/banyandb/docker-compose.yml
+++ b/test/e2e-v2/cases/banyandb/docker-compose.yml
@@ -13,25 +13,44 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# SWIP-15 BanyanDB self-observability e2e: a minimal CLUSTER (1 liaison + 1 hot data node), scraped
+# WITHOUT the FODC proxy. The OTel collector scrapes each node's own :2121 Prometheus endpoint
+# directly and injects the FODC-equivalent identity labels
+# (cluster / container_name / node_role / node_type / pod_name) as static per-scrape-job labels
+# (see otel-collector-config.yaml). The MAL rules read those tags regardless of origin, so the
+# cluster / instance / endpoint rule set is exercised without a FODC deployment.
+#
+# BanyanDB 0.11+ is required: the FODC-proxy cluster observability and the queue / lifecycle metric
+# families this feature reads were introduced after 0.10, and 0.11 uses file/DNS node discovery
+# (no etcd). The image is pinned per-case to the latest 0.11-dev build the public demo runs
+# (commit 8a1936ce9); the repo-wide ${SW_BANYANDB_COMMIT} is an older build that predates the
+# queue/lifecycle metric families.
services:
- oap:
+ data-hot:
extends:
file: ../../script/docker-compose/base-compose.yml
- service: oap
- expose:
- - 11800
- ports:
- - "11800:11800"
- - "12800:12800"
+ service: banyandb-data
+ image: "ghcr.io/apache/skywalking-banyandb:8a1936ce96653e89d3d13250a42abc6e3d42fae7-testing"
+ hostname: data-hot
+ command: data --node-discovery-mode=file --node-discovery-file-path=/etc/banyandb/nodes.yaml --node-labels type=hot
+ volumes:
+ - ./nodes.yaml:/etc/banyandb/nodes.yaml
networks:
- e2e
- banyandb:
+
+ liaison:
extends:
file: ../../script/docker-compose/base-compose.yml
- service: banyandb
- ports:
- - "17913:17913"
- - "2121:2121"
+ service: liaison
+ image: "ghcr.io/apache/skywalking-banyandb:8a1936ce96653e89d3d13250a42abc6e3d42fae7-testing"
+ command: liaison --node-discovery-mode=file --node-discovery-file-path=/etc/banyandb/nodes.yaml --data-node-selector type=hot
+ volumes:
+ - ./nodes.yaml:/etc/banyandb/nodes.yaml
+ depends_on:
+ data-hot:
+ condition: service_healthy
+ networks:
+ - e2e
otel-collector:
image: otel/opentelemetry-collector:${OTEL_COLLECTOR_VERSION}
@@ -42,6 +61,27 @@ services:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
expose:
- 55678
+ depends_on:
+ liaison:
+ condition: service_healthy
+ data-hot:
+ condition: service_healthy
+
+ oap:
+ extends:
+ file: ../../script/docker-compose/base-compose.yml
+ service: oap
+ environment:
+ SW_STORAGE: banyandb
+ SW_STORAGE_BANYANDB_TARGETS: "liaison:17912"
+ ports:
+ - "11800:11800"
+ - "12800:12800"
+ networks:
+ - e2e
+ depends_on:
+ liaison:
+ condition: service_healthy
networks:
e2e:
diff --git a/test/e2e-v2/cases/banyandb/e2e.yaml b/test/e2e-v2/cases/banyandb/e2e.yaml
index 965f8da653..96b4ba2371 100644
--- a/test/e2e-v2/cases/banyandb/e2e.yaml
+++ b/test/e2e-v2/cases/banyandb/e2e.yaml
@@ -44,7 +44,7 @@ cleanup:
on: failure
output-dir: $SW_INFRA_E2E_LOG_DIR/banyandb-data
items:
- - service: banyandb
+ - service: data-hot
paths:
- /tmp/trace/
- /tmp/stream/
@@ -52,3 +52,6 @@ cleanup:
- /tmp/property/
- /tmp/schema-property/
- /tmp/accesslog/
+ - service: liaison
+ paths:
+ - /tmp/accesslog/
diff --git a/test/e2e-v2/cases/banyandb/docker-compose.yml b/test/e2e-v2/cases/banyandb/expected/metrics-has-label-value.yml
similarity index 52%
copy from test/e2e-v2/cases/banyandb/docker-compose.yml
copy to test/e2e-v2/cases/banyandb/expected/metrics-has-label-value.yml
index 2b0d618a81..6fc7bffccc 100644
--- a/test/e2e-v2/cases/banyandb/docker-compose.yml
+++ b/test/e2e-v2/cases/banyandb/expected/metrics-has-label-value.yml
@@ -13,35 +13,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-services:
- oap:
- extends:
- file: ../../script/docker-compose/base-compose.yml
- service: oap
- expose:
- - 11800
- ports:
- - "11800:11800"
- - "12800:12800"
- networks:
- - e2e
- banyandb:
- extends:
- file: ../../script/docker-compose/base-compose.yml
- service: banyandb
- ports:
- - "17913:17913"
- - "2121:2121"
-
- otel-collector:
- image: otel/opentelemetry-collector:${OTEL_COLLECTOR_VERSION}
- networks:
- - e2e
- command: [ "--config=/etc/otel-collector-config.yaml" ]
- volumes:
- - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
- expose:
- - 55678
-
-networks:
- e2e:
+# For labeled metric series (e.g. reporting_instances by container_name, queue throughput by
+# operation): at least one result carries a non-empty label and at least one non-null value bucket.
+debuggingtrace: null
+type: TIME_SERIES_VALUES
+results:
+ {{- contains .results }}
+ - metric:
+ labels:
+ {{- contains .metric.labels }}
+ - key: {{ notEmpty .key }}
+ value: {{ notEmpty .value }}
+ {{- end }}
+ values:
+ {{- contains .values }}
+ - id: {{ notEmpty .id }}
+ value: {{ notEmpty .value }}
+ traceid: null
+ owner: null
+ - id: {{ notEmpty .id }}
+ value: null
+ traceid: null
+ owner: null
+ {{- end }}
+ {{- end}}
+error: null
diff --git a/test/e2e-v2/cases/banyandb/otel-collector-config.yaml b/test/e2e-v2/cases/banyandb/nodes.yaml
similarity index 59%
copy from test/e2e-v2/cases/banyandb/otel-collector-config.yaml
copy to test/e2e-v2/cases/banyandb/nodes.yaml
index 30d45627d2..9c0602822a 100644
--- a/test/e2e-v2/cases/banyandb/otel-collector-config.yaml
+++ b/test/e2e-v2/cases/banyandb/nodes.yaml
@@ -13,36 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-receivers:
- prometheus:
- config:
- scrape_configs:
- - job_name: "banyandb-monitoring"
- scrape_interval: 5s
- static_configs:
- - targets: ["banyandb:2121"]
- labels:
- host_name: server
-
-processors:
- batch:
-
-exporters:
- otlp:
- endpoint: oap:11800
- tls:
- insecure: true
- debug:
- verbosity: detailed
-
-service:
- pipelines:
- metrics:
- receivers:
- - prometheus
- processors:
- - batch
- exporters:
- - otlp
-
-
+# Static node-discovery file for the BanyanDB 0.11 cluster (file discovery mode; no etcd).
+nodes:
+ - name: data-hot
+ grpc_address: data-hot:17912
+ - name: liaison
+ grpc_address: liaison:17912
diff --git a/test/e2e-v2/cases/banyandb/otel-collector-config.yaml b/test/e2e-v2/cases/banyandb/otel-collector-config.yaml
index 30d45627d2..74016d2303 100644
--- a/test/e2e-v2/cases/banyandb/otel-collector-config.yaml
+++ b/test/e2e-v2/cases/banyandb/otel-collector-config.yaml
@@ -13,16 +13,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# No FODC proxy in this e2e: the collector scrapes each BanyanDB node's own :2121 Prometheus
+# endpoint directly and injects the identity labels the SWIP-15 MAL rules key on. On a real
+# FODC deployment these are stamped by the proxy; here they are static per-target scrape labels
+# (the same mechanism the previous single-node e2e used to inject host_name, extended to two
+# targets and the full identity set). The OTel Prometheus receiver maps the Prometheus `job` to
+# service.name, which OAP's receiver maps back to the `job_name` tag the rules filter on; all
+# other static labels arrive as datapoint attributes (tags). Hence:
+# - one job_name "banyandb-monitoring" (matches the filter on all three rule files)
+# - per-target: cluster (service key), container_name (role discriminator + instance key),
+# node_role, pod_name, and node_type (data only; liaison Elvis-defaults it to 'n/a').
receivers:
prometheus:
config:
scrape_configs:
- - job_name: "banyandb-monitoring"
+ - job_name: "banyandb-monitoring"
scrape_interval: 5s
static_configs:
- - targets: ["banyandb:2121"]
+ - targets: ["liaison:2121"]
labels:
- host_name: server
+ cluster: e2e-banyandb
+ container_name: liaison
+ node_role: ROLE_LIAISON
+ pod_name: banyandb-liaison-0
+ - targets: ["data-hot:2121"]
+ labels:
+ cluster: e2e-banyandb
+ container_name: data
+ node_role: ROLE_DATA
+ node_type: hot
+ pod_name: banyandb-data-hot-0
processors:
batch:
@@ -32,8 +52,6 @@ exporters:
endpoint: oap:11800
tls:
insecure: true
- debug:
- verbosity: detailed
service:
pipelines:
@@ -44,5 +62,3 @@ service:
- batch
exporters:
- otlp
-
-