You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add POST /inspect/values admin API to read foreign metric values (#13927)
A new admin-only POST /inspect/values reads the VALUES of a metric this OAP does not
define locally (persisted by another OAP), by running the real MQE engine over
caller-supplied {valueColumn, valueType} — the value-reading companion to the merged
GET /inspect/entities foreign path (#13926).
A request-scoped InspectQueryContext ThreadLocal makes the foreign metric look
registered to every read path (provide-if-absent — the local catalog always wins):
ValueColumnMetadata resolves its value column / type / scope, and the storage location
registries resolve where it lives — MetadataRegistry synthesizes a BanyanDB measure
schema, IndexController resolves the ES metrics-all index, TableHelper probes the JDBC
function tables. The storage read paths need no per-DAO special-casing; the response is
the native MQE ExpressionResult. Verified end-to-end across banyandb/es/postgresql.
Copy file name to clipboardExpand all lines: docs/en/changes/changes.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,6 +3,7 @@
3
3
#### Project
4
4
5
5
* Extend the `GET /inspect/entities` admin API to inspect a metric persisted by **any** OAP, even one this node does not define locally. When the metric is unknown to the local registry, the caller supplies `valueColumn` + `valueType` and the storage backend resolves the physical index/table/group from its own running config (no DB schema/table-metadata read): ES uses the merged `metrics-all` index + `metric_table` discriminator, JDBC probes the node's function tables by the `table_name` discriminator, and BanyanDB synthesizes a read-only measure schema. Scope is no longer required — the `entity_id` is decoded structurally (service / 2nd-level / relations) with a generic `name` leaf. Locally-defined metrics keep the exact field names, scope, and `mqeEntity` as before.
6
+
* Add the `POST /inspect/values` admin API — read the value series of a metric persisted by **another** OAP (one this node does not define locally) by supplying its `{valueColumn, valueType}`. The real MQE engine runs over a request-scoped `InspectQueryContext` overlay (provide-if-absent — the local catalog always wins) that makes the foreign metric look registered to every read path: `ValueColumnMetadata` resolves its value column / type / scope, and the storage location registries resolve where it lives (`MetadataRegistry` synthesizes a BanyanDB measure schema, `IndexController` resolves the ES `metrics-all` index, `TableHelper` probes the JDBC function tables), so the read returns the native MQE `ExpressionResult` with no per-DAO special-casing. Admin-only (a forced read this OAP cannot validate); not mirrored onto the public REST / GraphQL surface. See the [Inspect API](../setup/backend/admin-api/inspect.md).
6
7
* Remove the always-on alarm-to-event conversion (`EventHookCallback`). A triggered alarm is no longer synthesized into the events pipeline as an `Alarm`/`AlarmRecovery` event; events now originate only from real event sources (agents, SkyWalking CLI, Kubernetes Event Exporter). Alarms remain available through the alarm store (`getAlarm`/`queryAlarms`) and the configured alarm hooks. This drops a documented "Known Event" and removes 1-2 synthetic event records per alarm fire.
Reads the **values** of a metric this OAP does not define locally, by running the
234
+
real MQE engine over caller-supplied metadata. Where `GET /inspect/entities`
235
+
answers *which entities hold values*, this answers *what those values are* — the
236
+
native MQE `ExpressionResult` (the same shape the UI renders for a catalog metric),
237
+
for a metric that is otherwise foreign to this node.
238
+
239
+
Because it trusts caller-supplied metadata and forces a read of a metric this OAP
240
+
cannot validate, it is **admin-only** (it never mirrors onto the public REST /
241
+
GraphQL surface) and takes a request **body**: an MQE expression plus one metadata
242
+
entry per foreign metric the expression references.
243
+
244
+
Request body (`application/json`):
245
+
246
+
| Field | Required | Description |
247
+
|-------|----------|-------------|
248
+
|`expression`| yes | The MQE expression to evaluate — a single foreign metric name, or an expression combining foreign and/or catalog metrics. |
249
+
|`entity`| yes | The MQE query entity; its `scope` binds every foreign metric. e.g. `{ "scope": "Service", "serviceName": "X", "normal": true }` (use `serviceInstanceName` / `endpointName` for the deeper scopes). |
250
+
|`start` / `end`| yes | Time range, same format as [`/inspect/entities`](#get-inspectentities). |
251
+
|`step`| yes | One of `MINUTE` / `HOUR` / `DAY`. |
252
+
|`foreignMetrics`| yes | One entry per metric in `expression` that this OAP does not define: `{ "name": "...", "valueColumn": "value", "valueType": "LONG" }`. `valueColumn` is the post-override physical column; `valueType` is one of `LONG` / `INT` / `DOUBLE` / `LABELED`. A locally-defined metric must **not** be listed here (query it via the public GraphQL `execExpression`). |
253
+
254
+
The metadata is overlaid **provide-if-absent** (the local catalog always wins) onto
255
+
the same registries the engine already consults, so a foreign metric looks registered
256
+
for the duration of the request: `ValueColumnMetadata` resolves its value column / type
257
+
/ scope, and the storage location registries resolve its index / table / measure exactly
258
+
as described in [Foreign metrics](#foreign-metrics-not-defined-on-this-oap). The overlay
259
+
is request-scoped to the calling thread and removed when the read completes; the public
260
+
query path never sets it.
261
+
262
+
Only scalar (`LONG` / `INT` / `DOUBLE`) and labeled (best-effort) value series are
263
+
supported. An expression that resolves to `top_n` / records / heatmaps needs a local
264
+
model and surfaces as an error. Under ES `logicSharding=true` a foreign value read is
265
+
unsupported (the physical index derives from the metric's stream class), returning `500`.
266
+
267
+
Example — read the value series of `meter_custom_x`, defined on another OAP:
268
+
269
+
```bash
270
+
curl -X POST 'http://oap-admin:17128/inspect/values' \
## Discovering the OAP REST URL for the MQE follow-up
231
299
232
300
To keep the surface minimal, the inspect API does not introduce a separate
@@ -248,6 +316,11 @@ session start is enough.
248
316
| 400 |`{"error":"metric type SAMPLED_RECORD is out of scope for /inspect/entities"}`| Metric is `SAMPLED_RECORD`. |
249
317
| 400 |`{"error":"process scope is out of scope"}`| Scope is `Process` / `ProcessRelation`. |
250
318
| 400 |`{"error":"limit must be between 1 and 300"}`|`limit` out of range. |
319
+
| 400 |`{"error":"foreignMetrics is required; a locally-defined metric should be queried via the public GraphQL execExpression"}`|`POST /inspect/values` body had no `foreignMetrics`. |
320
+
| 400 |`{"error":"metric foo is defined locally; query it via the GraphQL execExpression and drop it from foreignMetrics"}`| A `foreignMetrics` entry names a metric this OAP already defines. |
321
+
| 400 |`{"error":"valueColumn is invalid: …"}`| A `foreignMetrics``valueColumn` is not a bare identifier. |
322
+
| 400 |`{"error":"<MQE error>"}`|`POST /inspect/values` expression resolved to an unsupported shape (e.g. `top_n` / record / heatmap) for a foreign metric. |
323
+
| 500 |`{"error":"<storage error>"}`| A wrong `valueColumn` / `valueType`, or ES `logicSharding=true`, surfaced at the storage layer during a value read. |
Copy file name to clipboardExpand all lines: oap-server/server-admin/inspect/src/main/java/org/apache/skywalking/oap/server/admin/inspect/handler/InspectRestHandler.java
0 commit comments