Skip to main content

monkit-metrics

Use when investigating latency, throughput, error rate, or concurrency of any Storj Go service (satellite, storagenode, gateway, linksharing, etc.), comparing performance across pods/nodes/regions, finding which Go functions are instrumented, or interpreting monkit `function` / `function_times` series from VictoriaMetrics/Prometheus.

Informations de source

Dépôt
storj/claude-plugins
Dernière activité de la source
9 octobre 2026 à 09:12
Langue détectée de SKILL.md
anglais
Étoiles
3
Forks
0

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
2 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
monkit-metrics
description
Use when investigating latency, throughput, error rate, or concurrency of any Storj Go service (satellite, storagenode, gateway, linksharing, etc.), comparing performance across pods/nodes/regions, finding which Go functions are instrumented, or interpreting monkit `function` / `function_times` series from VictoriaMetrics/Prometheus.
# Monkit Prometheus Metrics ## Overview Monkit (`defer mon.Task()(&ctx)(&err)`) emits **two** metrics for every instrumented Go method: - **`function_times`** — latency (percentiles, min/max, recent samples). Use for "how slow?" - **`function`** — call counters and concurrency. Use for "how often?" / "how many in flight?" / "how many fail?" Both share the same `name`, `scope`, and environment labels, so you usually query them with the same filter set. ## `function_times` — latency ### Structure ``` function_times{name="...", field="...", kind="...", scope="...", ...} ``` | Label | Values | Notes | |---|---|---| | `name` | `__Receiver__Method` or bare func name | See naming below | | `field` | see field table | Most useful: `r99`, `recent` | | `kind` | `success`, `failure` | Failure r99 can be 100×+ success r99 — always filter | | `scope` | package import path with `/` → `_` | e.g. `storj_io_storj_satellite_metainfo` | Values are in **seconds** — multiply by 1000 for ms. ### `field` values | Field | Meaning | Available where | |---|---|---| | `count`, `sum` | Cumulative since process start | **everywhere** | | `recent` | Most recent observation | **everywhere** | | `min`, `max` | Cumulative min/max | Prometheus-scraped envs only | | `r10`, `r50`, `r90`, `r99` | Percentile latency (rolling reservoir) | Prometheus-scraped envs only | | `rmin`, `rmax`, `ravg` | Min/max/avg over the rolling window | Prometheus-scraped envs only | #### Percentiles are NOT available in every environment Which `field` values exist depends on the **scrape path**, not the metric. Verified 2026-09-08 on `ffqrvx0pyhp8gf`: | `environment_name` | Scraped by | `field` values present | |---|---|---| | `storj-prod-satellite-us1` / `-eu1` / `-ap1` / `-slc` | in-cluster Prometheus | full set incl. `r10`–`r99`, `rmin`/`rmax`/`ravg`, `min`, `max` | | `storj-select`, `dp-prod-edge-*`, `storj-qa-satellite` | node-level **otelcol** | **only `count`, `recent`, `sum`** | So `field="r99"` silently returns **no series** for the select and edge fleets — and a no-data result looks identical to a wrong label name, which is an easy hour to lose. **Portable fallback that works in every environment — mean latency:** ```promql sum(rate(function_times{..., field="sum"}[10m])) / sum(rate(function_times{..., field="count"}[10m])) ``` Before assuming a field exists, check: ```promql count by (field, kind) (function_times{environment_name="...", scope="...", name="..."}) ``` ## `function` — call counters & concurrency Same `name`/`scope`/env labels as `function_times`. **No `kind` label** — outcomes are encoded in `field`. ### `field` values | Field | Kind | Meaning | How to query | |---|---|---|---| | `total` | counter | All invocations | `rate(...[5m])` = total QPS | | `successes` | counter | Non-error returns | `rate(...[5m])` = success QPS | | `failures` | counter | Error returns | `rate(...[5m])` = error rate | | `errors` | counter | Same as `failures` in current monkit | — | | `panics` | counter | Go panics caught by monkit | `rate(...[5m])` — should be ~0 | | `count` | counter | **Failures split by `error_name`** (e.g. `drpc_ResourceExhausted`); summed it equals `failures` (verified 2026-10-09 on satellite prod) | `sum by (error_name) (rate(...[5m]))` = which errors. Never use for call rate — use `total` | | `current` | gauge | In-flight invocations right now | Read as-is | | `highwater` | gauge | Peak `current` ever seen | Read as-is | | `delta` | counter-ish | Completions since last sample | Rarely useful — prefer `rate(total)` | ### When to use `function` vs `function_times` | Question | Metric / field | |---|---| | What's the request rate? | `rate(function{field="total"}[5m])` | | What's the error rate? | `rate(function{field="failures"}[5m])` | | Error percentage? | `rate(function{field="failures"}[5m]) / rate(function{field="total"}[5m])` | | Are we panicking? | `rate(function{field="panics"}[5m])` | | How many calls are in flight? | `function{field="current"}` | | Peak concurrency seen? | `function{field="highwater"}` | | How slow is it? | `function_times{field="r99", kind="success"}` | ### Example: compare QPS across regions ```promql sum by (environment_name) ( rate(function{ name="__Endpoint__CommitObject", scope="storj_io_storj_satellite_metainfo", field="successes" }[5m]) ) ``` ## Naming Convention | Go code | `name` label | |---|---| | `func (e *Endpoint) CommitObject(...)` | `__Endpoint__CommitObject` | | `func commitObject(...)` (package-level) | `commitObject` | | `func (s *SpannerAdapter) CommitObject(...)` | `__SpannerAdapter__CommitObject` | ## Same name, different scope The same `name` can appear under multiple `scope` values. Example: `__Endpoint__CommitObject` lives in `storj_io_storj_satellite_metainfo` (the handler), and the RPC wrapper for the same call shows up as `_metainfo_Metainfo_CommitObject` in `storj_io_common_rpc_rpctracing`. Always include `scope` (or `scope=~"..."`) when comparing or you will mix unrelated timings. > Note: Storj scopes double — `storj_io_storj_satellite_metainfo` — because the import path is `storj.io/storj/...`. Expected. ## Finding Instrumented Methods Methods with `defer mon.Task()(&ctx)(&err)` emit metrics: ```bash rg "defer mon\.Task\(\)" satellite/metainfo/ rg -n "func.*CommitObject|defer mon\.Task" satellite/metainfo/endpoint_object.go ``` To trace direct callees: find method calls in the function body, locate their definitions, check for `defer mon.Task()`. ## Querying via Grafana MCP This is the primary path — the datasource sits behind Grafana auth, so direct `curl` won't work. ### Datasource UIDs Use **`ffqrvx0pyhp8gf`** — VictoriaMetrics (victoria.intra.storj.tools), the default Prometheus datasource. It carries monkit `function` / `function_times` for **all** four prod satellites plus the select fleet, the edge/gateway fleet and QA. Confirmed 2026-09-08. That is the only metrics datasource in use. Infrastructure metrics live here too, under their own job labels — e.g. the Crunchbits-PA TiDB cluster is `{job="tidb_cluster", server_name=~"cb-prod-us1-pa-[1-7]"}`. Verify with `mcp__grafana-cloud__list_datasources(type="prometheus")` if this changes. ### Discovery workflow ```text # 1. Confirm the metric exists in this datasource mcp__grafana-cloud__list_prometheus_metric_names( datasourceUid="ffqrvx0pyhp8gf", regex="function_times") # 2. Discover real label values (don't guess `environment_name`s) mcp__grafana-cloud__list_prometheus_label_values( datasourceUid="ffqrvx0pyhp8gf", labelName="environment_name", matches=[{"filters":[{"name":"__name__","type":"=","value":"function_times"}]}]) # 3. Query mcp__grafana-cloud__query_prometheus( datasourceUid="ffqrvx0pyhp8gf", expr='function_times{name="__Endpoint__CommitObject", scope=~".*satellite_metainfo", field="r99", kind="success"}', queryType="range", startTime="now-1h", endTime="now", stepSeconds=60) ``` - `queryType="instant"` → single point right now. Use to sanity-check a label combo exists. - `queryType="range"` → time series. Requires `startTime`, `endTime`, `stepSeconds`. - Times accept RFC3339 (`2026-05-19T22:00:00Z`) or relative (`now`, `now-1h`, `now-30m`). - Use `mcp__grafana-cloud__generate_deeplink` to hand the engineer a Grafana Explore URL when reporting findings. ### Correlating with logs Production satellite logs are in ClickHouse `otel.otel_logs` (Grafana datasource `cfqvdb4b1y96oa`), not VictoriaLogs. See the `satellite-observability` skill for the query recipe. ### Direct HTTP fallback Only when Prometheus is reachable without auth (local Prometheus, dev cluster): ```bash curl -sG 'http://HOST:9090/api/v1/query' \ --data-urlencode 'query=function_times{name="__Endpoint__CommitObject",field="r99",kind="success"}' ``` ## Comparing instances / regions Multiple series per `name` (one per pod). Aggregate before computing stats. **PromQL — let Prometheus do the work:** ```promql quantile by (environment_name, name) ( 0.99, function_times{name=~"...", field="r99", kind="success"} ) * 1000 ``` **Python — reusable pattern:** see `compare-instances.py` in this directory (range query + aggregate across pods, two endpoints). ## Common Mistakes | Mistake | Fix | |---|---| | Filtering `kind="error"` on `function_times` | Use `kind="failure"`. `error` returns zero series. | | No `kind` filter on `function_times` | `failure` r99 can dwarf `success` r99 for the same name | | Filtering `kind="..."` on `function` | `function` has no `kind` label — use `field="successes"` / `"failures"` instead | | Using `function_times{field="count"}` for QPS | Works, but `function{field="successes"}` (or `"total"`) is the idiomatic call-rate counter | | Forgetting `rate()` on counter fields | `total`, `successes`, `failures`, `errors`, `panics`, `count` are counters — always wrap in `rate(...[Xm])` | | Using `rate()` on gauge fields | `current` and `highwater` are gauges — query directly, not via `rate()` | | Same `name` in multiple scopes | Add `scope=~"..."` to disambiguate | | Comparing raw series across pods | Aggregate (sum/avg/quantile) by the labels you care about | | Averaging `r10`/`r50`/`r90`/`r99` across pods | Averaging percentiles is mathematically meaningless. Use `quantile by (...)` or `max by (...)` — never `avg by` on percentile fields. | | Forgetting seconds → ms | `function_times` values are seconds — multiply by 1000 | | Missing instrumented methods | Check both `success` and `failure` kinds (on `function_times`) or `field="failures"` (on `function`) | | `sum by (name)` on a plain metric | `name` is a label only on `function`, `function_times` and `tagged_*` series. For plain metrics use `by (__name__)` — otherwise several metrics silently merge into one line. Dividing two `by (__name__)` vectors drops the name; use one query per metric | | Assuming `field="r99"` exists | Percentiles are absent on otelcol-scraped envs (`storj-select`, `dp-prod-edge-*`, QA). Check `count by (field)` first; fall back to `rate(sum)/rate(count)` |
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub