feat: PoC Prometheus/Mimir streaming Search API over websocket - #248
Draft
itsmylife wants to merge 44 commits into
Draft
feat: PoC Prometheus/Mimir streaming Search API over websocket#248itsmylife wants to merge 44 commits into
itsmylife wants to merge 44 commits into
Conversation
…metrics explorer and monaco Wire label-value autocomplete (MetricsLabelsSection), the metrics explorer (MetricsModalContext) and Monaco metric completions to the search[] path when the streaming search API is active, instead of regexifying typed text into match[] / client-side fuzzy. Guarded by hasServerSideSearch() so behavior is unchanged when the toggle is off. Co-authored-by: Cursor <cursoragent@cursor.com>
Streaming search returns scored autocomplete suggestions, so SearchApiClient now caps the requested limit at SEARCH_API_DEFAULTS.limit (10000) and never forwards the larger series limit (DEFAULT_SERIES_LIMIT=40000) that callers commonly pass. 0 keeps its "unlimited" meaning; explicit smaller limits are honored. Co-authored-by: Cursor <cursoragent@cursor.com>
…er, builder and monaco Add progressive Observable search methods (streamMetrics/streamLabelKeys/streamLabelValues) to the language provider that emit accumulating results as NDJSON batches stream in, with a single-emission fallback when the streaming API is inactive. The metrics browser selectors now render server-side fuzzy/scored results progressively as the user types, the builder routes label-name search to search[] when capable, and Monaco metric completions consume the streaming Observable. All paths are guarded so behavior is unchanged when streaming is off. Co-authored-by: Cursor <cursoragent@cursor.com>
# Conflicts: # docker-compose.yaml # provisioning/datasources/datasources.yml # provisioning/prometheus/prometheus.yml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
This is an exploration / proof-of-concept, not a merge candidate. It is a large "monster" branch that we will not merge as-is — the delivery plan (how this gets split into small, reviewable PRs) will be tracked separately. Please review it as a design spike, not as final code.
What & why
Adds experimental support for Prometheus/Mimir's new NDJSON streaming search API (
/api/v1/search/{metric_names,label_names,label_values}, gated upstream by--enable-feature=search-api, shipped in Prometheus 3.13.0).Today autocomplete injects the user's typed text into
match[]as a regex (and sometimes runs a client-side fuzzy filter). This PR routes typed text into the newsearch[]param instead, giving fuzzy, scored, server-side metric/label/value suggestions that stream in progressively as they are computed.The whole feature is strictly gated behind a per-datasource
enableSearchApitoggle and falls back to the existing labels/series clients whenever the toggle is off, the server is unsupported, or Grafana Live is unavailable — so default behavior is unchanged.The streaming transport is a persistent WebSocket via Grafana Live. This datasource has never used a socket-based transport before, so this is a significant new dependency and operational surface (Live must be enabled, websockets must be reachable through any proxies/LB, HA node-affinity considerations, etc.).
This is intentional for the streaming PoC, but we plan to introduce an alternative solution that does not require a socket (e.g. a plain HTTP streaming/chunked path) so the feature does not hard-depend on Grafana Live. That socket-free design will come as a follow-up — feedback on the transport choice is very welcome here.
Architecture
Rather than one Live channel per query, we open one long-lived bidirectional channel per browser session (
search/<sessionNonce>) and run a request/response protocol over it:{requestId, slotId, endpoint, params}per (debounced) search.RunStreamidles on an in-process mailbox, performs the upstream NDJSON read, and streams frames back down the same channel tagged withrequestId.endpoint + slotId), so independent widgets/panels sharing one datasource instance don't cancel each other; a bounded number of slots run concurrently.RunStream, own mailbox) — frames never leak to org-mates.Dev environment / how to test
The repo ships a ready-to-run Phase 0 environment: a dev Prometheus with the search API flag on, plus Grafana with Live enabled and two provisioned datasources (one with the toggle on, one off for comparison).
Build the backend (produces the
dist/gpx_*binaries):Build the frontend (or use
yarn devfor watch mode):Start the stack (Grafana + dev Prometheus via docker compose):
yarn server # == docker compose up --buildThis runs:
prom/prometheus:v3.13.1with--enable-feature=search-api(pinned — thelatesttag has a known bug that can resolve to a pre-search-api release).GF_LIVE_MAX_CONNECTIONS=100(Live/websocket is required for the transport) and the externalized plugin loaded in place of core Prometheus.Sanity-check the upstream API returns NDJSON ending in a trailer line:
Exercise the feature in Grafana (http://localhost:3000):
prometheus-search-apidatasource (toggle on). Open Explore / the query builder and type in the metric combobox, label filters, metrics browser, and the metrics explorer modal — you should see fuzzy, scored suggestions stream in as you type.search/<uuid>publish/frame traffic (not a new connection per keystroke).prometheusdatasource (toggle off) and confirm the classic labels/series autocomplete behavior is unchanged.What to focus a review on
The parts most worth scrutiny (highest risk / most novel):
metric_names/label_names/label_values), strictsearch/<uuid>channel-path validation, param allowlist + bounds, and the fact that forwarded per-request auth is rejected on the stream path (service-level datasource auth only). Seepkg/promlib/stream.go+pkg/promlib/resource/search_stream.go.cancel-previous, bounded concurrent slots, mailbox startup race + reconnect idempotency, and cleanup on stream stop /InstanceDisposer. This is where subtle leaks/races would live.[]/partial snapshot).Delivery plan
This branch is a proof of concept. It will not be merged in one shot. The plan for how to break it into small, independently reviewable PRs (backend stream handlers, frontend client, per-component UI wiring, dev-env, socket-free alternative, etc.) will be shared separately.