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.

معلومات المصدر

المستودع
storj/claude-plugins
آخر نشاط في المصدر
٩ أكتوبر ٢٠٢٦ في ٠٩:١٢
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٣
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
2 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
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)` |
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub