Prometheus Metrics
Rindexer exposes Prometheus metrics for production observability. These metrics help you monitor indexing performance, RPC health, database operations, and stream delivery in real-time.
Overview
The metrics endpoint provides insight into:
- Indexing progress - Events processed, blocks indexed, sync status
- RPC performance - Request latencies, success/error rates, in-flight requests
- Database operations - Query durations, operation counts, connection pool status
- Stream delivery - Message counts, delivery latencies by stream type
- Chain state - Reorg detection, block lag behind chain head
Metrics Endpoint
Metrics are served on the same port as the health server (default 8080) at the /metrics path.
# Metrics available when indexer is running
curl http://localhost:8080/metricsThe endpoint returns metrics in Prometheus text exposition format, compatible with Prometheus, Grafana, Datadog, and other monitoring tools.
Configuration
Configure the metrics port using health_port in your rindexer.yaml:
global:
health_port: 9090 # Metrics available at http://localhost:9090/metricsAvailable Metrics
Indexing Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
rindexer_events_processed_total | Counter | network, contract, event | Total events processed |
rindexer_blocks_indexed_total | Counter | network, contract, event | Total blocks indexed |
rindexer_last_synced_block | Gauge | network, contract, event | Last synced block number |
rindexer_latest_chain_block | Gauge | network | Latest block on chain |
rindexer_blocks_behind | Gauge | network, contract, event | Blocks behind chain head |
rindexer_active_indexing_tasks | Gauge | - | Currently active indexing tasks |
rindexer_events_processed_total{contract="USDC",event="Transfer",network="ethereum"} 19971
rindexer_last_synced_block{contract="USDC",event="Transfer",network="ethereum"} 21901064
rindexer_blocks_behind{contract="USDC",event="Transfer",network="ethereum"} 2425731RPC Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
rindexer_rpc_requests_total | Counter | network, method, status | Total RPC requests (success/error) |
rindexer_rpc_request_duration_seconds | Histogram | network, method | RPC request latency |
rindexer_rpc_requests_in_flight | Gauge | network | Currently pending RPC requests |
Histogram buckets: 10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 2.5s, 5s, 10s
Example output:rindexer_rpc_requests_total{method="eth_getLogs",network="mainnet",status="success"} 156
rindexer_rpc_requests_total{method="eth_getLogs",network="mainnet",status="error"} 2
rindexer_rpc_request_duration_seconds_sum{method="eth_getLogs",network="mainnet"} 45.23
rindexer_rpc_request_duration_seconds_count{method="eth_getLogs",network="mainnet"} 156Database Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
rindexer_db_operations_total | Counter | operation, status | Total database operations |
rindexer_db_operation_duration_seconds | Histogram | operation | Database operation latency |
rindexer_db_pool_connections | Gauge | database, state | Connection pool size (active/idle) |
rindexer_atomic_batches_total | Counter | status, reason | Atomic no-code batches (custom-table operations, journal, raw rows and cursor in one transaction) by outcome |
Operation types: query, insert, update, delete, batch_insert, batch_execute
Atomic batch labels: status is committed or rolled_back; reason is ok on commit and otherwise deadlock, cursor_missing or error. A rolled_back batch is harmless (nothing was written and the batch is retried), so alert on the rate of reason="deadlock" rather than on rollbacks as such. Recorded only on the Postgres-only custom-tables path (see Delivery Semantics).
Histogram buckets: 1ms, 5ms, 10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 2.5s
Stream Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
rindexer_stream_messages_total | Counter | stream_type, status | Total messages sent |
rindexer_stream_message_duration_seconds | Histogram | stream_type | Message delivery latency |
Stream types: sns, webhook, rabbitmq, kafka, redis, cloudflare_queues
Chain State Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
rindexer_reorgs_detected_total | Counter | network | Chain reorganizations detected |
rindexer_reorg_depth | Gauge | network | Depth of last detected reorg |
Shared Tip Logs Metrics
Live indexing reads each new tip block once per network with an unfiltered eth_getLogs and serves every stream from that answer (see shared_tip_logs). rindexer_rpc_requests_total{method="eth_getLogs"} keeps counting both the shared and the per-stream calls.
| Metric | Type | Labels | Description |
|---|---|---|---|
rindexer_shared_tip_logs_requests_total | Counter | network, status | Unfiltered calls issued by the fetcher: success, error or timeout |
rindexer_shared_tip_logs_blocks_total | Counter | network, outcome | Terminal state per scheduled block: ready, empty_bloom_zero (zero header bloom, no call), empty_unverified (bloom checks disabled, empty answer accepted), gave_up (still empty after the retry budget), error, hash_mismatch or invalidated (dropped by a reorg signal while pending) |
rindexer_shared_tip_logs_empty_retries_total | Counter | network | Retries issued because a block with a non-zero logs bloom answered empty |
rindexer_shared_tip_logs_recovered_total | Counter | network | Blocks that became ready on a retry after an empty answer |
rindexer_shared_tip_logs_served_total | Counter | network, mode | Stream windows served from the cache: window, tip or prefix_rpc_plus_tip |
rindexer_shared_tip_logs_fallbacks_total | Counter | network, reason | Stream windows that used their own eth_getLogs: gave_up, error, hash_mismatch, wait_timeout or not_scheduled |
rindexer_shared_tip_logs_fetch_seconds | Histogram | network | Seconds from the first observation of a head block to its terminal state |
rindexer_shared_tip_logs_cache_blocks | Gauge | network | Head blocks resident in the per-network cache (at most cache_blocks) |
The network label of these series is the chain name, the same label rindexer_rpc_requests_total carries
(polygon for chain id 137), so the two can be subtracted to count the per-stream tip calls that remain.
Histogram buckets: 50ms, 100ms, 250ms, 500ms, 1s, 2s, 3s, 5s, 8s, 12s
Build Info
| Metric | Type | Labels | Description |
|---|---|---|---|
rindexer_build_info | Gauge | version | Build information (always 1) |
Prometheus Integration
Basic Prometheus Config
Add rindexer to your prometheus.yml:
scrape_configs:
- job_name: 'rindexer'
static_configs:
- targets: ['localhost:8080']
scrape_interval: 15s
metrics_path: /metricsDocker Compose Example
services:
rindexer:
image: your-rindexer-image
ports:
- "8080:8080"
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=adminUseful PromQL Queries
Indexing Progress
# Events per second (rate over 5 minutes)
rate(rindexer_events_processed_total[5m])
# Blocks behind chain head
rindexer_blocks_behind
# Sync progress percentage
(rindexer_last_synced_block / rindexer_latest_chain_block) * 100RPC Health
# RPC error rate
rate(rindexer_rpc_requests_total{status="error"}[5m])
/ rate(rindexer_rpc_requests_total[5m])
# P99 RPC latency
histogram_quantile(0.99, rate(rindexer_rpc_request_duration_seconds_bucket[5m]))
# RPC requests per second by method
rate(rindexer_rpc_requests_total[5m])Database Performance
# Database operation latency (P95)
histogram_quantile(0.95, rate(rindexer_db_operation_duration_seconds_bucket[5m]))
# Database error rate
rate(rindexer_db_operations_total{status="error"}[5m])
# Atomic batch deadlock rate (each one is a rolled back, retried batch)
rate(rindexer_atomic_batches_total{reason="deadlock"}[5m])Stream Delivery
# Stream delivery rate by type
rate(rindexer_stream_messages_total{status="success"}[5m])
# Stream error rate
rate(rindexer_stream_messages_total{status="error"}[5m])Alerting Examples
Prometheus Alerting Rules
groups:
- name: rindexer
rules:
- alert: RindexerHighBlockLag
expr: rindexer_blocks_behind > 1000
for: 5m
labels:
severity: warning
annotations:
summary: "Rindexer falling behind chain head"
description: "{{ $labels.contract }} is {{ $value }} blocks behind"
- alert: RindexerHighRPCErrorRate
expr: rate(rindexer_rpc_requests_total{status="error"}[5m]) > 0.1
for: 2m
labels:
severity: critical
annotations:
summary: "High RPC error rate detected"
- alert: RindexerReorgDetected
expr: increase(rindexer_reorgs_detected_total[5m]) > 0
labels:
severity: warning
annotations:
summary: "Chain reorg detected on {{ $labels.network }}"Grafana Dashboard
Import these panels for a basic rindexer dashboard:
- Sync Progress - Gauge showing
rindexer_blocks_behind - Events/sec - Graph of
rate(rindexer_events_processed_total[1m]) - RPC Latency - Heatmap of
rindexer_rpc_request_duration_seconds - Error Rates - Graph of error rates for RPC, DB, and streams
- Active Tasks - Stat panel for
rindexer_active_indexing_tasks
Best Practices
- Set appropriate scrape intervals - 15-30 seconds is typical for indexer metrics
- Monitor block lag - Alert when
rindexer_blocks_behindexceeds acceptable thresholds - Track RPC errors - High error rates may indicate provider issues or rate limiting
- Watch reorg metrics - Frequent reorgs may indicate network instability
- Use labels wisely - Filter by
network,contract,eventfor granular insights