prometheus/prometheus
Remote write, remote read, and OTLP
Active contributors: cstyan, bwplotka, tomwilkie, alexgreenbank, ArthurSens, jesusvazquez
Purpose
storage/remote/ is the largest sibling of tsdb/. It implements:
- Outbound remote write — ship samples to a remote storage backend over HTTP.
- Inbound remote write receiver — accept v1 or v2 protobuf payloads from another Prometheus or compatible sender.
- OTLP receiver — accept OpenTelemetry Protocol metrics and translate them into Prometheus's internal model.
- Remote read — pull historical series from a remote backend as if they were local.
- AzureAD / Google IAM helpers — pluggable HTTP client decorators for cloud-managed identity.
Directory layout
storage/remote/
├── storage.go # remote.Storage: top-level Storage that wraps queues + read clients.
├── queue_manager.go # The QueueManager: shards, batching, send loop, backoff (~2,300 lines).
├── stats.go # EWMA + sharding heuristics.
├── ewma.go # Exponentially weighted moving average implementation.
├── intern.go # String interning for label values.
├── max_timestamp.go # Atomic max-timestamp tracker.
├── client.go # HTTP client used by writes.
├── codec.go # Protobuf <-> internal format conversions.
├── chunked.go # Chunked HTTP response framing for streaming reads.
├── dial_context.go # Custom dial helpers (TLS, proxy).
├── metadata_watcher.go # Forwards metric metadata to remote write v1.
├── read.go / read_handler.go # Remote read client + server handlers.
├── write.go / write_handler.go # Outbound + inbound write.
├── write_otlp_handler.go # OTLP receiver.
├── azuread/ # Azure managed identity auth.
├── googleiam/ # GCE / GKE managed identity auth.
└── otlptranslator/ # OTLP -> Prometheus translation (delegated to prometheusremotewrite).Outbound remote write
sequenceDiagram
participant Append as Storage Appender
participant QM as QueueManager
participant Watcher as wlog.Watcher
participant Shards as N shards
participant Receiver
Note over Append,Watcher: Two ingest paths feed the queue
Append-->>QM: Append(sample)
Watcher->>QM: walRecord (server mode)
QM->>Shards: hash(seriesRef) % N
Shards->>Shards: build batch (size or time)
Shards->>Receiver: HTTP POST /api/v1/write (v1)<br/>or /api/v1/write?version=2 (v2)
Receiver-->>Shards: 2xx / 4xx / 5xx
QM->>QM: shard count adjustment (EWMA)QueueManager (in queue_manager.go) is the heart. Each RemoteWriteConfig produces one queue. Key behaviours:
- Sharding: series are hashed into
Nshards, sized bymin_shards <= N <= max_shards. The shard count is adjusted every 10 seconds based on an EWMA of the sample backlog (shardUpdateDuration,shardToleranceFraction = 0.3). - Batching: each shard buffers samples until either
max_samples_per_send(default 2000) orbatch_send_deadline(default 5s). - Compression: snappy is used for v1; v2 supports the same compression set as the WAL (
util/compression). - Backpressure: if all shards are full,
QueueManager.Appendreturnsfalseand drops the sample, incrementingprometheus_remote_storage_samples_dropped_total{reason="too_old"|"queue_full"|...}. - Retry: transient (5xx, network) errors retry with exponential backoff. 4xx other than 429 fail-fast, dropping the batch.
In server mode, the WAL watcher (tsdb/wlog/watcher.go) reads the WAL forwards and feeds the queue manager so a sample sent via remote write is durable on disk before it is enqueued. In agent mode, the same watcher is the only path.
Remote write protocol versions
| Version | Wire format | Notes |
|---|---|---|
| v1.0 | prompb.WriteRequest (snappy) |
Original; samples + optional metadata via MetadataWatcher. |
| v2 (RW2) | prompb/io/prometheus/write/v2.Request |
Per-sample exemplars, native histograms, NHCB, types/units in-band. |
The receiver discovers what message types the sender supports via the X-Prometheus-Remote-Write-Version header and a content-type negotiation.
OTLP receiver
/api/v1/otlp/v1/metrics (POST) accepts OpenTelemetry metric exports. The handler in write_otlp_handler.go translates each OTLP sum/gauge/histogram/summary into a Prometheus appender call via the otlptranslator/ sub-package, which delegates to the upstream github.com/prometheus/otlptranslator library.
OTLP-specific behaviour:
- Delta -> cumulative conversion is opt-in (
--storage.otlp.convert-deltaflag, behind--enable-feature=otlp-deltatocumulative). - Native delta ingestion (preserve delta temporality) is opt-in (
--enable-feature=otlp-native-delta-ingestion). - Lookback delta is honoured during conversion.
- The
__name__attribute is filtered out to prevent duplicate labels (3.10 fix#17917).
Remote read
The remote read protocol is the inverse: a Prometheus issues ReadRequests with a label matcher and time range, and the remote endpoint streams back chunks. Two transports:
- Buffered protobuf (legacy,
read_handler.go): one big response. - Chunked streaming (
chunked.go):application/x-streamed-protobuf;proto=prometheus.ChunkedReadResponse— interleaved chunks.
Local Prometheus exposes the read endpoint at /api/v1/read (used by other Prometheus instances or Mimir/Thanos for federation). The client lives in read.go.
OTLP translator
storage/remote/otlptranslator/prometheusremotewrite/ is a thin wrapper around the upstream github.com/prometheus/otlptranslator library. The translator owns:
- Metric name sanitisation (UTF-8 to Prometheus-friendly).
- Label name escaping.
- Histogram bucket conversion (classical to native).
- Summary expansion (sum/count/quantile).
Self-metrics
Per-queue (label remote_name, url):
prometheus_remote_storage_samples_in_totalprometheus_remote_storage_samples_pendingprometheus_remote_storage_samples_failed_total{reason}prometheus_remote_storage_samples_retried_totalprometheus_remote_storage_samples_dropped_total{reason}prometheus_remote_storage_shards{state}—desired,min,maxprometheus_remote_storage_shard_capacityprometheus_remote_storage_sent_batch_duration_secondsprometheus_remote_storage_highest_timestamp_in_secondsprometheus_remote_storage_string_interner_zero_reference_releases_total
Inbound (receiver):
prometheus_remote_storage_received_samples_totalprometheus_remote_storage_received_histograms_total
Configuration surface
remote_write:
- url: https://mimir.example.com/api/v1/push
name: mimir
queue_config:
capacity: 2500
max_samples_per_send: 2000
max_shards: 200
min_shards: 1
batch_send_deadline: 5s
metadata_config:
send: true
send_interval: 1m
write_relabel_configs:
- action: drop
source_labels: [__name__]
regex: 'noisy_metric.*'
sigv4: { region: us-east-1 } # AWS sigv4 authCLI flags surface OTLP-specific behaviours:
--web.enable-otlp-receiver— enable the OTLP endpoint.--storage.otlp.translation-strategy=NoUTF8EscapingWithSuffixes|UnderscoreEscapingWithSuffixes(3.x).
Entry points for modification
- Tweak shard heuristics:
queue_manager.go::calculateDesiredShards. Performance changes need before/afterprombenchruns. - Add an auth method: create a sub-package similar to
azuread/and register it instorage.go::ApplyConfig. - OTLP translation rule: the translator is an external library; for in-tree behaviour (delta/cumulative, naming), edit
write_otlp_handler.goand the option struct.
See Storage for the interface contracts and Remote write feature page for the operational view.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.