From dd3b222782c68f6a365ce80af1bdb7349f941b95 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier Date: Thu, 9 Jul 2026 16:08:32 +0200 Subject: [PATCH 1/4] alertmanager: group by alertname, repeat 5m to 12h (RpcProviderErrorRate spammed slack every 5min) --- .../monitoring/alertmanager/alertmanager.yml.tmpl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/infrastructure/monitoring/alertmanager/alertmanager.yml.tmpl b/infrastructure/monitoring/alertmanager/alertmanager.yml.tmpl index fd0f9799..42083bef 100644 --- a/infrastructure/monitoring/alertmanager/alertmanager.yml.tmpl +++ b/infrastructure/monitoring/alertmanager/alertmanager.yml.tmpl @@ -2,10 +2,15 @@ global: resolve_timeout: 5m route: - group_by: ['alertname', 'chain', 'aggregator'] - group_wait: 10s - group_interval: 30s - repeat_interval: 5m + # Group by alertname ONLY: one Slack message listing every (provider, + # chain) pair instead of 7 parallel groups each re-notifying on its own + # clock. repeat_interval 5m -> 12h: a still-firing alert is a reminder, + # not a new event (2026-07-09: RpcProviderErrorRate spammed the channel + # every 5 minutes for hours). + group_by: ['alertname'] + group_wait: 30s + group_interval: 15m + repeat_interval: 12h # Default: dedicated OCB Slack channel (direct incoming webhook). receiver: 'slack-direct' routes: From c09d095a9a3da75ae863a59394f40315b0c5264a Mon Sep 17 00:00:00 2001 From: Florent Tapponnier Date: Thu, 9 Jul 2026 16:13:28 +0200 Subject: [PATCH 2/4] rpc: delist nodies (avalanche/gnosis went paid) + merkle (base/bnb datacenter-IP lockouts), exclude documented-dead cloudflare from error-rate alert --- benchmarks/avalanche-rpc.yml | 27 ++--------------- benchmarks/base-rpc.yml | 28 ++--------------- benchmarks/bnb-rpc.yml | 28 ++--------------- benchmarks/gnosis-rpc.yml | 30 ++----------------- .../rpc-capabilities/cmd/script/config.go | 4 --- .../monitoring/prometheus/alert_rules.yml | 7 +++-- 6 files changed, 14 insertions(+), 110 deletions(-) diff --git a/benchmarks/avalanche-rpc.yml b/benchmarks/avalanche-rpc.yml index d86167cc..c7459546 100644 --- a/benchmarks/avalanche-rpc.yml +++ b/benchmarks/avalanche-rpc.yml @@ -26,7 +26,7 @@ methodology: - "Call-result classification: `ok` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "Archive depth: every 5 minutes we issue `eth_getBalance` at (head − depth) for depths from Geth's default pruned cap up to 5M blocks, exposing which free endpoints actually serve historical state." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, methodology and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to chain=\"avalanche\". Provider coverage: 6 no-key endpoints (PublicNode, dRPC, Tenderly, Nodies, Avalanche). Exclusions follow the cluster-wide rules documented on the parent benchmark." + - "Chain scope: every query on this page is pinned to chain=\"avalanche\". Provider coverage: 5 no-key endpoints (PublicNode, dRPC, Tenderly, Avalanche). Nodies was delisted 2026-07 when its Avalanche endpoint moved behind a paid plan. Exclusions follow the cluster-wide rules documented on the parent benchmark." findings: - "{{best_name}} currently leads free Avalanche RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 5 measured providers." @@ -37,7 +37,7 @@ faq: - q: "What is the fastest free Avalanche RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 5 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which Avalanche RPCs work without an API key?" - a: "The 6 providers on this page: PublicNode, dRPC, Tenderly, Nodies, Avalanche. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." + a: "The 5 providers on this page: PublicNode, dRPC, Tenderly, Avalanche. Nodies was delisted in July 2026 when its Avalanche endpoint moved behind a paid plan. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." - q: "Does the fastest Avalanche RPC change by region?" a: "Frequently. The headline number averages three probe origins (us-east, eu-west, Singapore), but per-region leaders regularly diverge, a gateway that wins from Virginia can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number on the page to a single origin; pick the one closest to where your requests actually originate." - q: "How is Avalanche RPC latency measured here?" @@ -133,29 +133,6 @@ providers: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="tenderly", chain="avalanche", region="sgp"}) series: avg_over_time(rpc_latency_milliseconds{provider="tenderly", chain="avalanche", region="sgp"}[1h]) - - slug: nodies - name: Nodies - tag: POKT Network's decentralized public RPC successor, 7+ chains - formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Nodies's no-key Avalanche endpoint." - queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="avalanche"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="nodies", chain="avalanche"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="nodies", chain="avalanche"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="nodies", chain="avalanche"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="nodies", chain="avalanche"}) / sum(ocb:rpc_call:rate_24h{provider="nodies", chain="avalanche"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="nodies", chain="avalanche"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="avalanche"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="avalanche", region="us-east"}) - series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="avalanche", region="us-east"}[1h]) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="avalanche", region="eu-west"}) - series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="avalanche", region="eu-west"}[1h]) - - region: ap-southeast - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="avalanche", region="sgp"}) - series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="avalanche", region="sgp"}[1h]) - - slug: avalanche-official name: Avalanche tag: Ava Labs C-Chain public RPC, Avalanche C-Chain only diff --git a/benchmarks/base-rpc.yml b/benchmarks/base-rpc.yml index e648d43c..4851067c 100644 --- a/benchmarks/base-rpc.yml +++ b/benchmarks/base-rpc.yml @@ -26,18 +26,17 @@ methodology: - "Call-result classification: `ok` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "Archive depth: every 5 minutes we issue `eth_getBalance` at (head − depth) for depths from Geth's default pruned cap up to 5M blocks, exposing which free endpoints actually serve historical state." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, methodology and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to chain=\"base\". Provider coverage: 6 no-key endpoints (PublicNode, dRPC, Tenderly, Nodies, Merkle, Base). Exclusions follow the cluster-wide rules documented on the parent benchmark." + - "Chain scope: every query on this page is pinned to chain=\"base\". Provider coverage: 5 no-key endpoints (PublicNode, dRPC, Tenderly, Nodies, Base). Merkle was delisted 2026-07 under the cluster lockout rule: it blocks our datacenter probe IPs, the same behavior that already excludes it on Ethereum. Exclusions follow the cluster-wide rules documented on the parent benchmark." findings: - "{{best_name}} currently leads free Base RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 6 measured providers." - - "Base is one of only two chains where {{name:merkle}} qualifies no-key (with BNB); its distribution is among the tightest in the whole cluster, p99 barely above p50." - "The official `mainnet.base.org` and the multi-chain gateways trade the lead depending on region, a reminder that \"fastest\" is a per-origin question, not a global one." faq: - q: "What is the fastest free Base RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 6 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which Base RPCs work without an API key?" - a: "The 6 providers on this page: PublicNode, dRPC, Tenderly, Nodies, Merkle, Base. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." + a: "The 5 providers on this page: PublicNode, dRPC, Tenderly, Nodies, Base. Merkle was delisted in July 2026: it blocks datacenter probe IPs, the same lockout behavior that already excludes it on Ethereum. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." - q: "Does the fastest Base RPC change by region?" a: "Frequently. The headline number averages three probe origins (us-east, eu-west, Singapore), but per-region leaders regularly diverge, a gateway that wins from Virginia can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number on the page to a single origin; pick the one closest to where your requests actually originate." - q: "How is Base RPC latency measured here?" @@ -156,29 +155,6 @@ providers: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="base", region="sgp"}) series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="base", region="sgp"}[1h]) - - slug: merkle - name: Merkle - tag: Base + BSC public no-key gateway (Ethereum hit by Cloudflare 20-min lockout, excluded) - formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Merkle's no-key Base endpoint." - queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="merkle", chain="base"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="merkle", chain="base"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="merkle", chain="base"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="merkle", chain="base"}) / sum(ocb:rpc_call:rate_24h{provider="merkle", chain="base"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="merkle", chain="base"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="us-east"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="us-east"}[1h]) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="eu-west"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="eu-west"}[1h]) - - region: ap-southeast - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="sgp"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="sgp"}[1h]) - - slug: base-official name: Base tag: Coinbase-operated, Base mainnet RPC diff --git a/benchmarks/bnb-rpc.yml b/benchmarks/bnb-rpc.yml index 6bd90aa6..5772f6e3 100644 --- a/benchmarks/bnb-rpc.yml +++ b/benchmarks/bnb-rpc.yml @@ -26,18 +26,17 @@ methodology: - "Call-result classification: `ok` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "Archive depth: every 5 minutes we issue `eth_getBalance` at (head − depth) for depths from Geth's default pruned cap up to 5M blocks, exposing which free endpoints actually serve historical state." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, methodology and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to chain=\"bnb\". Provider coverage: 5 no-key endpoints (PublicNode, dRPC, Nodies, Merkle, Binance). Exclusions follow the cluster-wide rules documented on the parent benchmark." + - "Chain scope: every query on this page is pinned to chain=\"bnb\". Provider coverage: 4 no-key endpoints (PublicNode, dRPC, Nodies, Binance). Merkle was delisted 2026-07 under the cluster lockout rule: it blocks our datacenter probe IPs, the same behavior that already excludes it on Ethereum. Exclusions follow the cluster-wide rules documented on the parent benchmark." findings: - "{{best_name}} currently leads free BNB Chain RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 5 measured providers." - "Binance's dataseed is a single-chain specialist: blisteringly fast near its home regions (single-digit milliseconds from us-east at times) and 10x slower from Singapore, the widest regional spread in the cluster." - - "{{name:merkle}} qualifies here (BNB is one of its two stable no-key chains) and brings its signature tight distribution to a field otherwise dominated by the dataseed's regional extremes." faq: - q: "What is the fastest free BNB Chain RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 5 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which BNB Chain RPCs work without an API key?" - a: "The 5 providers on this page: PublicNode, dRPC, Nodies, Merkle, Binance. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." + a: "The 4 providers on this page: PublicNode, dRPC, Nodies, Binance. Merkle was delisted in July 2026: it blocks datacenter probe IPs, the same lockout behavior that already excludes it on Ethereum. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." - q: "Does the fastest BNB Chain RPC change by region?" a: "Frequently. The headline number averages three probe origins (us-east, eu-west, Singapore), but per-region leaders regularly diverge, a gateway that wins from Virginia can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number on the page to a single origin; pick the one closest to where your requests actually originate." - q: "How is BNB Chain RPC latency measured here?" @@ -133,29 +132,6 @@ providers: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="bnb", region="sgp"}) series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="bnb", region="sgp"}[1h]) - - slug: merkle - name: Merkle - tag: Base + BSC public no-key gateway (Ethereum hit by Cloudflare 20-min lockout, excluded) - formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Merkle's no-key BNB Chain endpoint." - queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="merkle", chain="bnb"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="merkle", chain="bnb"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="merkle", chain="bnb"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="merkle", chain="bnb"}) / sum(ocb:rpc_call:rate_24h{provider="merkle", chain="bnb"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="merkle", chain="bnb"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="us-east"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="us-east"}[1h]) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="eu-west"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="eu-west"}[1h]) - - region: ap-southeast - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="sgp"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="sgp"}[1h]) - - slug: binance name: Binance tag: BNB Chain dataseed RPC, Binance-operated diff --git a/benchmarks/gnosis-rpc.yml b/benchmarks/gnosis-rpc.yml index 07d673c6..e252e3ba 100644 --- a/benchmarks/gnosis-rpc.yml +++ b/benchmarks/gnosis-rpc.yml @@ -14,7 +14,7 @@ unit: ms higher_is_better: false seo_intro: | - Gnosis is the cluster's clearest proof that official does not mean fast: the chain-official `rpc.gnosischain.com` is the slowest endpoint we measure on the chain, around 433 ms p50 on the 3-region average, while five third-party gateways beat it, including Nodies, whose POKT-backed infrastructure reaches Gnosis as its only chain in this 12-chain expansion. 5 no-key providers, the same `eth_getBlockByNumber` call every 60 seconds, three regions. + Gnosis is the cluster's clearest proof that official does not mean fast: the chain-official `rpc.gnosischain.com` is the slowest endpoint we measure on the chain, around 433 ms p50 on the 3-region average, while several third-party gateways beat it. 4 no-key providers, the same `eth_getBlockByNumber` call every 60 seconds, three regions. abstract: | Per-chain member of the RPC latency cluster. We measure the round-trip latency of a single, identical RPC call (`eth_getBlockByNumber`) against every no-key public Gnosis endpoint that sustains continuous probing, 6 providers, every 60 seconds, from us-east, eu-west and Singapore. The harness also classifies every response (ok / http_err / jsonrpc_err / stale / timeout) and audits archive depth every 5 minutes, so the leaderboard rewards sustained, honest availability rather than a fast error message. The cross-chain view lives on the parent rpc-capabilities benchmark; this page is the Gnosis-scoped answer with per-region breakdowns as a first-class dimension. @@ -26,19 +26,18 @@ methodology: - "Call-result classification: `ok` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "Archive depth: every 5 minutes we issue `eth_getBalance` at (head − depth) for depths from Geth's default pruned cap up to 5M blocks, exposing which free endpoints actually serve historical state." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, methodology and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to chain=\"gnosis\". Provider coverage: 6 no-key endpoints (PublicNode, dRPC, Tenderly, Nodies, Gnosis). Exclusions follow the cluster-wide rules documented on the parent benchmark." + - "Chain scope: every query on this page is pinned to chain=\"gnosis\". Provider coverage: 5 no-key endpoints (PublicNode, dRPC, Tenderly, Gnosis). Nodies was delisted 2026-07 when its Gnosis endpoint moved behind a paid plan. Exclusions follow the cluster-wide rules documented on the parent benchmark." findings: - "{{best_name}} currently leads free Gnosis RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 5 measured providers." - "The chain-official endpoint anchors the wrong end of the board: ~433 ms p50 with a similar profile from every region, slower than every gateway on this page. It is honest about its blocks; it is just slow." - - "{{name:nodies}} ({{p50:nodies}}) is the quiet story of the expansion: Gnosis is the only long-tail chain it qualifies on, and it serves the chain well, a POKT-routed gateway beating the house endpoint by a wide margin." - "{{name:drpc}} ({{p50:drpc}}) shows its usual anycast consistency here, part of the pattern that has it leading 10 of the 12 long-tail chains on the 3-region average." faq: - q: "What is the fastest free Gnosis RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 5 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which Gnosis RPCs work without an API key?" - a: "The 6 providers on this page: PublicNode, dRPC, Tenderly, Nodies, Gnosis. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." + a: "The 5 providers on this page: PublicNode, dRPC, Tenderly, Gnosis. Nodies was delisted in July 2026 when its Gnosis endpoint moved behind a paid plan. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." - q: "Does the fastest Gnosis RPC change by region?" a: "Frequently. The headline number averages three probe origins (us-east, eu-west, Singapore), but per-region leaders regularly diverge, a gateway that wins from Virginia can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number on the page to a single origin; pick the one closest to where your requests actually originate." - q: "How is Gnosis RPC latency measured here?" @@ -134,29 +133,6 @@ providers: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="tenderly", chain="gnosis", region="sgp"}) series: avg_over_time(rpc_latency_milliseconds{provider="tenderly", chain="gnosis", region="sgp"}[1h]) - - slug: nodies - name: Nodies - tag: POKT Network's decentralized public RPC successor, 7+ chains - formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Nodies's no-key Gnosis endpoint." - queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="gnosis"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="nodies", chain="gnosis"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="nodies", chain="gnosis"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="nodies", chain="gnosis"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="nodies", chain="gnosis"}) / sum(ocb:rpc_call:rate_24h{provider="nodies", chain="gnosis"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="nodies", chain="gnosis"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="gnosis"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="gnosis", region="us-east"}) - series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="gnosis", region="us-east"}[1h]) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="gnosis", region="eu-west"}) - series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="gnosis", region="eu-west"}[1h]) - - region: ap-southeast - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="gnosis", region="sgp"}) - series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="gnosis", region="sgp"}[1h]) - - slug: gnosis-official name: Gnosis tag: Gnosis chain-official RPC (rpc.gnosischain.com) diff --git a/harnesses/rpc-capabilities/cmd/script/config.go b/harnesses/rpc-capabilities/cmd/script/config.go index 6597167b..d23fe91d 100644 --- a/harnesses/rpc-capabilities/cmd/script/config.go +++ b/harnesses/rpc-capabilities/cmd/script/config.go @@ -143,7 +143,6 @@ func chains() []Chain { {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_BASE_DRPC", "https://base.drpc.org")}, {Slug: "tenderly", Name: "Tenderly Gateway", URL: envDefault("RPC_URL_BASE_TENDERLY", "https://gateway.tenderly.co/public/base")}, {Slug: "nodies", Name: "Nodies (POKT)", URL: envDefault("RPC_URL_BASE_NODIES", "https://base-pokt.nodies.app")}, - {Slug: "merkle", Name: "Merkle", URL: envDefault("RPC_URL_BASE_MERKLE", "https://base.merkle.io")}, {Slug: "base-official", Name: "Base Official", URL: envDefault("RPC_URL_BASE_OFFICIAL", "https://mainnet.base.org")}, }, }, @@ -155,7 +154,6 @@ func chains() []Chain { {Slug: "publicnode", Name: "PublicNode", URL: envDefault("RPC_URL_BNB_PUBLICNODE", "https://bsc-rpc.publicnode.com")}, {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_BNB_DRPC", "https://bsc.drpc.org")}, {Slug: "nodies", Name: "Nodies (POKT)", URL: envDefault("RPC_URL_BNB_NODIES", "https://bsc-pokt.nodies.app")}, - {Slug: "merkle", Name: "Merkle", URL: envDefault("RPC_URL_BNB_MERKLE", "https://bsc.merkle.io")}, {Slug: "binance", Name: "Binance Official", URL: envDefault("RPC_URL_BNB_OFFICIAL", "https://bsc-dataseed1.binance.org")}, }, }, @@ -169,7 +167,6 @@ func chains() []Chain { {Slug: "publicnode", Name: "PublicNode", URL: envDefault("RPC_URL_AVALANCHE_PUBLICNODE", "https://avalanche-c-chain-rpc.publicnode.com")}, {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_AVALANCHE_DRPC", "https://avalanche.drpc.org")}, {Slug: "tenderly", Name: "Tenderly Gateway", URL: envDefault("RPC_URL_AVALANCHE_TENDERLY", "https://gateway.tenderly.co/public/avalanche")}, - {Slug: "nodies", Name: "Nodies (POKT)", URL: envDefault("RPC_URL_AVALANCHE_NODIES", "https://avax-pokt.nodies.app/ext/bc/C/rpc")}, {Slug: "avalanche-official", Name: "Avalanche Official", URL: envDefault("RPC_URL_AVALANCHE_OFFICIAL", "https://api.avax.network/ext/bc/C/rpc")}, }, }, @@ -224,7 +221,6 @@ func chains() []Chain { {Slug: "publicnode", Name: "PublicNode", URL: envDefault("RPC_URL_GNOSIS_PUBLICNODE", "https://gnosis-rpc.publicnode.com")}, {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_GNOSIS_DRPC", "https://gnosis.drpc.org")}, {Slug: "tenderly", Name: "Tenderly Gateway", URL: envDefault("RPC_URL_GNOSIS_TENDERLY", "https://gateway.tenderly.co/public/gnosis")}, - {Slug: "nodies", Name: "Nodies (POKT)", URL: envDefault("RPC_URL_GNOSIS_NODIES", "https://gnosis-pokt.nodies.app")}, {Slug: "gnosis-official", Name: "Gnosis Official", URL: envDefault("RPC_URL_GNOSIS_OFFICIAL", "https://rpc.gnosischain.com")}, }, }, diff --git a/infrastructure/monitoring/prometheus/alert_rules.yml b/infrastructure/monitoring/prometheus/alert_rules.yml index f06632b1..65dc877c 100644 --- a/infrastructure/monitoring/prometheus/alert_rules.yml +++ b/infrastructure/monitoring/prometheus/alert_rules.yml @@ -133,10 +133,13 @@ groups: # throttling incident (2026-07-08) ran for days unalerted. Fires # when a (provider, chain) cell errors on >50% of calls for 1h. - alert: RpcProviderErrorRate + # cloudflare exclu: mort documenté (permissioned mode, -32046 sur + # eth_getBlockByNumber) qu'on garde probé exprès comme cautionary + # tale sur la page ethereum-rpc. Pas besoin de rappel Slack. expr: | ( - sum by (provider, chain) (rate(rpc_call_total{result!="ok"}[30m])) - / sum by (provider, chain) (rate(rpc_call_total[30m])) + sum by (provider, chain) (rate(rpc_call_total{result!="ok",provider!="cloudflare"}[30m])) + / sum by (provider, chain) (rate(rpc_call_total{provider!="cloudflare"}[30m])) ) > 0.5 for: 1h labels: From 394b3a874db272ad8e2529d07fbceadabe3c6362 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier Date: Thu, 9 Jul 2026 16:45:56 +0200 Subject: [PATCH 3/4] rpc: re-list merkle base/bnb via residential proxy (blocks datacenter IPs), disclosed on row --- benchmarks/base-rpc.yml | 27 +++++++++++- benchmarks/bnb-rpc.yml | 27 +++++++++++- .../rpc-capabilities/cmd/script/config.go | 7 +++ .../rpc-capabilities/cmd/script/probe.go | 43 ++++++++++++++++++- 4 files changed, 98 insertions(+), 6 deletions(-) diff --git a/benchmarks/base-rpc.yml b/benchmarks/base-rpc.yml index 4851067c..3b338eed 100644 --- a/benchmarks/base-rpc.yml +++ b/benchmarks/base-rpc.yml @@ -26,7 +26,7 @@ methodology: - "Call-result classification: `ok` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "Archive depth: every 5 minutes we issue `eth_getBalance` at (head − depth) for depths from Geth's default pruned cap up to 5M blocks, exposing which free endpoints actually serve historical state." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, methodology and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to chain=\"base\". Provider coverage: 5 no-key endpoints (PublicNode, dRPC, Tenderly, Nodies, Base). Merkle was delisted 2026-07 under the cluster lockout rule: it blocks our datacenter probe IPs, the same behavior that already excludes it on Ethereum. Exclusions follow the cluster-wide rules documented on the parent benchmark." + - "Chain scope: every query on this page is pinned to chain=\"base\". Provider coverage: 6 no-key endpoints (PublicNode, dRPC, Tenderly, Nodies, Merkle, Base). Merkle blocks datacenter IPs since 2026-07, so it is probed through a residential proxy; its latency includes proxy overhead and is flagged as such. Exclusions follow the cluster-wide rules documented on the parent benchmark." findings: - "{{best_name}} currently leads free Base RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 6 measured providers." @@ -36,7 +36,7 @@ faq: - q: "What is the fastest free Base RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 6 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which Base RPCs work without an API key?" - a: "The 5 providers on this page: PublicNode, dRPC, Tenderly, Nodies, Base. Merkle was delisted in July 2026: it blocks datacenter probe IPs, the same lockout behavior that already excludes it on Ethereum. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." + a: "The 6 providers on this page: PublicNode, dRPC, Tenderly, Nodies, Merkle, Base. Merkle blocks datacenter IPs since July 2026, so its probes route through a residential proxy; the latency column includes that proxy overhead and the row is flagged accordingly. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." - q: "Does the fastest Base RPC change by region?" a: "Frequently. The headline number averages three probe origins (us-east, eu-west, Singapore), but per-region leaders regularly diverge, a gateway that wins from Virginia can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number on the page to a single origin; pick the one closest to where your requests actually originate." - q: "How is Base RPC latency measured here?" @@ -155,6 +155,29 @@ providers: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="base", region="sgp"}) series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="base", region="sgp"}[1h]) + - slug: merkle + name: Merkle + tag: Blocks datacenter IPs since 2026-07; probed via residential proxy, latency includes proxy overhead + formula: "50th percentile over 24h of round-trip latency (ms) for `eth_getBlockByNumber` every 60s from 3 regions via a residential proxy (Merkle blocks datacenter IPs since 2026-07); includes proxy overhead, not directly comparable." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="merkle", chain="base"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="merkle", chain="base"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="merkle", chain="base"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="merkle", chain="base"}) / sum(ocb:rpc_call:rate_24h{provider="merkle", chain="base"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="merkle", chain="base"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="sgp"}[1h]) + - slug: base-official name: Base tag: Coinbase-operated, Base mainnet RPC diff --git a/benchmarks/bnb-rpc.yml b/benchmarks/bnb-rpc.yml index 5772f6e3..68a5cad6 100644 --- a/benchmarks/bnb-rpc.yml +++ b/benchmarks/bnb-rpc.yml @@ -26,7 +26,7 @@ methodology: - "Call-result classification: `ok` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "Archive depth: every 5 minutes we issue `eth_getBalance` at (head − depth) for depths from Geth's default pruned cap up to 5M blocks, exposing which free endpoints actually serve historical state." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, methodology and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to chain=\"bnb\". Provider coverage: 4 no-key endpoints (PublicNode, dRPC, Nodies, Binance). Merkle was delisted 2026-07 under the cluster lockout rule: it blocks our datacenter probe IPs, the same behavior that already excludes it on Ethereum. Exclusions follow the cluster-wide rules documented on the parent benchmark." + - "Chain scope: every query on this page is pinned to chain=\"bnb\". Provider coverage: 5 no-key endpoints (PublicNode, dRPC, Nodies, Merkle, Binance). Merkle blocks datacenter IPs since 2026-07, so it is probed through a residential proxy; its latency includes proxy overhead and is flagged as such. Exclusions follow the cluster-wide rules documented on the parent benchmark." findings: - "{{best_name}} currently leads free BNB Chain RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 5 measured providers." @@ -36,7 +36,7 @@ faq: - q: "What is the fastest free BNB Chain RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 5 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which BNB Chain RPCs work without an API key?" - a: "The 4 providers on this page: PublicNode, dRPC, Nodies, Binance. Merkle was delisted in July 2026: it blocks datacenter probe IPs, the same lockout behavior that already excludes it on Ethereum. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." + a: "The 5 providers on this page: PublicNode, dRPC, Nodies, Merkle, Binance. Merkle blocks datacenter IPs since July 2026, so its probes route through a residential proxy; the latency column includes that proxy overhead and the row is flagged accordingly. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." - q: "Does the fastest BNB Chain RPC change by region?" a: "Frequently. The headline number averages three probe origins (us-east, eu-west, Singapore), but per-region leaders regularly diverge, a gateway that wins from Virginia can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number on the page to a single origin; pick the one closest to where your requests actually originate." - q: "How is BNB Chain RPC latency measured here?" @@ -132,6 +132,29 @@ providers: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="bnb", region="sgp"}) series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="bnb", region="sgp"}[1h]) + - slug: merkle + name: Merkle + tag: Blocks datacenter IPs since 2026-07; probed via residential proxy, latency includes proxy overhead + formula: "50th percentile over 24h of round-trip latency (ms) for `eth_getBlockByNumber` every 60s from 3 regions via a residential proxy (Merkle blocks datacenter IPs since 2026-07); includes proxy overhead, not directly comparable." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="merkle", chain="bnb"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="merkle", chain="bnb"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="merkle", chain="bnb"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="merkle", chain="bnb"}) / sum(ocb:rpc_call:rate_24h{provider="merkle", chain="bnb"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="merkle", chain="bnb"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="sgp"}[1h]) + - slug: binance name: Binance tag: BNB Chain dataseed RPC, Binance-operated diff --git a/harnesses/rpc-capabilities/cmd/script/config.go b/harnesses/rpc-capabilities/cmd/script/config.go index d23fe91d..33761881 100644 --- a/harnesses/rpc-capabilities/cmd/script/config.go +++ b/harnesses/rpc-capabilities/cmd/script/config.go @@ -23,6 +23,11 @@ type Provider struct { Slug string Name string URL string + // ViaProxy routes probes through RPC_PROBE_PROXY_URL (residential + // rotating proxy). For providers that block datacenter IPs (merkle + // since 2026-07). Latency then includes proxy overhead; the spec + // discloses it on the row. + ViaProxy bool } type Chain struct { @@ -143,6 +148,7 @@ func chains() []Chain { {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_BASE_DRPC", "https://base.drpc.org")}, {Slug: "tenderly", Name: "Tenderly Gateway", URL: envDefault("RPC_URL_BASE_TENDERLY", "https://gateway.tenderly.co/public/base")}, {Slug: "nodies", Name: "Nodies (POKT)", URL: envDefault("RPC_URL_BASE_NODIES", "https://base-pokt.nodies.app")}, + {Slug: "merkle", Name: "Merkle", URL: envDefault("RPC_URL_BASE_MERKLE", "https://base.merkle.io"), ViaProxy: true}, {Slug: "base-official", Name: "Base Official", URL: envDefault("RPC_URL_BASE_OFFICIAL", "https://mainnet.base.org")}, }, }, @@ -154,6 +160,7 @@ func chains() []Chain { {Slug: "publicnode", Name: "PublicNode", URL: envDefault("RPC_URL_BNB_PUBLICNODE", "https://bsc-rpc.publicnode.com")}, {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_BNB_DRPC", "https://bsc.drpc.org")}, {Slug: "nodies", Name: "Nodies (POKT)", URL: envDefault("RPC_URL_BNB_NODIES", "https://bsc-pokt.nodies.app")}, + {Slug: "merkle", Name: "Merkle", URL: envDefault("RPC_URL_BNB_MERKLE", "https://bsc.merkle.io"), ViaProxy: true}, {Slug: "binance", Name: "Binance Official", URL: envDefault("RPC_URL_BNB_OFFICIAL", "https://bsc-dataseed1.binance.org")}, }, }, diff --git a/harnesses/rpc-capabilities/cmd/script/probe.go b/harnesses/rpc-capabilities/cmd/script/probe.go index 620eee81..eb0e1308 100644 --- a/harnesses/rpc-capabilities/cmd/script/probe.go +++ b/harnesses/rpc-capabilities/cmd/script/probe.go @@ -6,13 +6,49 @@ import ( "encoding/json" "fmt" "io" + "log" "net/http" + "net/url" + "os" "strconv" "strings" "sync" "time" ) +// probeProxyClient returns a client routing through RPC_PROBE_PROXY_URL +// (residential rotating proxy) for providers that block datacenter IPs. +// Falls back to a direct client if the env is unset, which surfaces as a +// visible 100% error rate rather than a silent measurement change. +var proxyClientOnce sync.Once +var proxyClientCached *http.Client + +func probeProxyClient() *http.Client { + proxyClientOnce.Do(func() { + raw := strings.TrimSpace(os.Getenv("RPC_PROBE_PROXY_URL")) + if raw == "" { + log.Printf("[PROBE] WARNING: ViaProxy provider configured but RPC_PROBE_PROXY_URL is unset; probing direct") + proxyClientCached = &http.Client{Timeout: probeTimeout} + return + } + u, err := url.Parse(raw) + if err != nil { + log.Printf("[PROBE] WARNING: invalid RPC_PROBE_PROXY_URL: %v; probing direct", err) + proxyClientCached = &http.Client{Timeout: probeTimeout} + return + } + proxyClientCached = &http.Client{ + Timeout: probeTimeout, + Transport: &http.Transport{ + Proxy: http.ProxyURL(u), + DisableKeepAlives: true, // fresh proxy IP per probe + MaxIdleConnsPerHost: 0, + }, + } + }) + return proxyClientCached +} + const ( // 60s (was 30s): the cluster probes ~23 chains per provider from ONE // IP per region. Keyless per-IP budgets (1RPC daily cap, dRPC CU/s) @@ -92,7 +128,7 @@ type blockHeader struct { // on cache hits rather than real RPC work. Fetching the full latest // header with a rotating request id defeats body-keyed edge caches; // the header's `number` field keeps the staleness check intact. -func callLatestBlock(ctx context.Context, url string) (block uint64, result string, latencyMs float64, err error) { +func callLatestBlock(ctx context.Context, url string, viaProxy bool) (block uint64, result string, latencyMs float64, err error) { body := []byte(fmt.Sprintf( `{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest",false],"id":%d}`, time.Now().UnixNano(), @@ -101,6 +137,9 @@ func callLatestBlock(ctx context.Context, url string) (block uint64, result stri req.Header.Set("Content-Type", "application/json") req.Header.Set("User-Agent", "OpenChainBench/1.0 (+https://openchainbench.com)") client := &http.Client{Timeout: probeTimeout} + if viaProxy { + client = probeProxyClient() + } start := time.Now() resp, err := client.Do(req) @@ -175,7 +214,7 @@ func probeOne(ctx context.Context, c Chain, p Provider) { tick := func() { probeCtx, cancel := context.WithTimeout(ctx, probeTimeout) defer cancel() - block, result, latency, err := callLatestBlock(probeCtx, p.URL) + block, result, latency, err := callLatestBlock(probeCtx, p.URL, p.ViaProxy) if result == "ok" { tips.update(c.Slug, block) From 5e7ef7e4a860fb1a5c4778a8f67f6d67deeda306 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier Date: Thu, 9 Jul 2026 16:56:25 +0200 Subject: [PATCH 4/4] rpc: merkle delisted for the real reason (no eth_getBlockByNumber, -32601), revert proxy plumbing (wrong diagnosis) --- benchmarks/base-rpc.yml | 27 +----------- benchmarks/bnb-rpc.yml | 27 +----------- .../rpc-capabilities/cmd/script/config.go | 7 --- .../rpc-capabilities/cmd/script/probe.go | 43 +------------------ 4 files changed, 6 insertions(+), 98 deletions(-) diff --git a/benchmarks/base-rpc.yml b/benchmarks/base-rpc.yml index 3b338eed..55f0510b 100644 --- a/benchmarks/base-rpc.yml +++ b/benchmarks/base-rpc.yml @@ -26,7 +26,7 @@ methodology: - "Call-result classification: `ok` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "Archive depth: every 5 minutes we issue `eth_getBalance` at (head − depth) for depths from Geth's default pruned cap up to 5M blocks, exposing which free endpoints actually serve historical state." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, methodology and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to chain=\"base\". Provider coverage: 6 no-key endpoints (PublicNode, dRPC, Tenderly, Nodies, Merkle, Base). Merkle blocks datacenter IPs since 2026-07, so it is probed through a residential proxy; its latency includes proxy overhead and is flagged as such. Exclusions follow the cluster-wide rules documented on the parent benchmark." + - "Chain scope: every query on this page is pinned to chain=\"base\". Provider coverage: 5 no-key endpoints (PublicNode, dRPC, Tenderly, Nodies, Base). Merkle was delisted 2026-07: its gateway does not serve `eth_getBlockByNumber`, the anti-cache probe this cluster standardized on, so it cannot be measured on equal terms. Exclusions follow the cluster-wide rules documented on the parent benchmark." findings: - "{{best_name}} currently leads free Base RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 6 measured providers." @@ -36,7 +36,7 @@ faq: - q: "What is the fastest free Base RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 6 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which Base RPCs work without an API key?" - a: "The 6 providers on this page: PublicNode, dRPC, Tenderly, Nodies, Merkle, Base. Merkle blocks datacenter IPs since July 2026, so its probes route through a residential proxy; the latency column includes that proxy overhead and the row is flagged accordingly. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." + a: "The 5 providers on this page: PublicNode, dRPC, Tenderly, Nodies, Base. Merkle was delisted in July 2026 because its gateway does not serve `eth_getBlockByNumber` (JSON-RPC -32601), the anti-cache probe used across the cluster, so its latency cannot be compared on equal terms. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." - q: "Does the fastest Base RPC change by region?" a: "Frequently. The headline number averages three probe origins (us-east, eu-west, Singapore), but per-region leaders regularly diverge, a gateway that wins from Virginia can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number on the page to a single origin; pick the one closest to where your requests actually originate." - q: "How is Base RPC latency measured here?" @@ -155,29 +155,6 @@ providers: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="base", region="sgp"}) series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="base", region="sgp"}[1h]) - - slug: merkle - name: Merkle - tag: Blocks datacenter IPs since 2026-07; probed via residential proxy, latency includes proxy overhead - formula: "50th percentile over 24h of round-trip latency (ms) for `eth_getBlockByNumber` every 60s from 3 regions via a residential proxy (Merkle blocks datacenter IPs since 2026-07); includes proxy overhead, not directly comparable." - queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="merkle", chain="base"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="merkle", chain="base"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="merkle", chain="base"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="merkle", chain="base"}) / sum(ocb:rpc_call:rate_24h{provider="merkle", chain="base"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="merkle", chain="base"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="us-east"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="us-east"}[1h]) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="eu-west"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="eu-west"}[1h]) - - region: ap-southeast - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="sgp"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="sgp"}[1h]) - - slug: base-official name: Base tag: Coinbase-operated, Base mainnet RPC diff --git a/benchmarks/bnb-rpc.yml b/benchmarks/bnb-rpc.yml index 68a5cad6..d85ecd35 100644 --- a/benchmarks/bnb-rpc.yml +++ b/benchmarks/bnb-rpc.yml @@ -26,7 +26,7 @@ methodology: - "Call-result classification: `ok` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "Archive depth: every 5 minutes we issue `eth_getBalance` at (head − depth) for depths from Geth's default pruned cap up to 5M blocks, exposing which free endpoints actually serve historical state." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, methodology and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to chain=\"bnb\". Provider coverage: 5 no-key endpoints (PublicNode, dRPC, Nodies, Merkle, Binance). Merkle blocks datacenter IPs since 2026-07, so it is probed through a residential proxy; its latency includes proxy overhead and is flagged as such. Exclusions follow the cluster-wide rules documented on the parent benchmark." + - "Chain scope: every query on this page is pinned to chain=\"bnb\". Provider coverage: 4 no-key endpoints (PublicNode, dRPC, Nodies, Binance). Merkle was delisted 2026-07: its gateway does not serve `eth_getBlockByNumber`, the anti-cache probe this cluster standardized on, so it cannot be measured on equal terms. Exclusions follow the cluster-wide rules documented on the parent benchmark." findings: - "{{best_name}} currently leads free BNB Chain RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 5 measured providers." @@ -36,7 +36,7 @@ faq: - q: "What is the fastest free BNB Chain RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 5 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which BNB Chain RPCs work without an API key?" - a: "The 5 providers on this page: PublicNode, dRPC, Nodies, Merkle, Binance. Merkle blocks datacenter IPs since July 2026, so its probes route through a residential proxy; the latency column includes that proxy overhead and the row is flagged accordingly. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." + a: "The 4 providers on this page: PublicNode, dRPC, Nodies, Binance. Merkle was delisted in July 2026 because its gateway does not serve `eth_getBlockByNumber` (JSON-RPC -32601), the anti-cache probe used across the cluster, so its latency cannot be compared on equal terms. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." - q: "Does the fastest BNB Chain RPC change by region?" a: "Frequently. The headline number averages three probe origins (us-east, eu-west, Singapore), but per-region leaders regularly diverge, a gateway that wins from Virginia can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number on the page to a single origin; pick the one closest to where your requests actually originate." - q: "How is BNB Chain RPC latency measured here?" @@ -132,29 +132,6 @@ providers: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="bnb", region="sgp"}) series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="bnb", region="sgp"}[1h]) - - slug: merkle - name: Merkle - tag: Blocks datacenter IPs since 2026-07; probed via residential proxy, latency includes proxy overhead - formula: "50th percentile over 24h of round-trip latency (ms) for `eth_getBlockByNumber` every 60s from 3 regions via a residential proxy (Merkle blocks datacenter IPs since 2026-07); includes proxy overhead, not directly comparable." - queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="merkle", chain="bnb"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="merkle", chain="bnb"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="merkle", chain="bnb"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="merkle", chain="bnb"}) / sum(ocb:rpc_call:rate_24h{provider="merkle", chain="bnb"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="merkle", chain="bnb"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="us-east"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="us-east"}[1h]) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="eu-west"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="eu-west"}[1h]) - - region: ap-southeast - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="sgp"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="sgp"}[1h]) - - slug: binance name: Binance tag: BNB Chain dataseed RPC, Binance-operated diff --git a/harnesses/rpc-capabilities/cmd/script/config.go b/harnesses/rpc-capabilities/cmd/script/config.go index 33761881..d23fe91d 100644 --- a/harnesses/rpc-capabilities/cmd/script/config.go +++ b/harnesses/rpc-capabilities/cmd/script/config.go @@ -23,11 +23,6 @@ type Provider struct { Slug string Name string URL string - // ViaProxy routes probes through RPC_PROBE_PROXY_URL (residential - // rotating proxy). For providers that block datacenter IPs (merkle - // since 2026-07). Latency then includes proxy overhead; the spec - // discloses it on the row. - ViaProxy bool } type Chain struct { @@ -148,7 +143,6 @@ func chains() []Chain { {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_BASE_DRPC", "https://base.drpc.org")}, {Slug: "tenderly", Name: "Tenderly Gateway", URL: envDefault("RPC_URL_BASE_TENDERLY", "https://gateway.tenderly.co/public/base")}, {Slug: "nodies", Name: "Nodies (POKT)", URL: envDefault("RPC_URL_BASE_NODIES", "https://base-pokt.nodies.app")}, - {Slug: "merkle", Name: "Merkle", URL: envDefault("RPC_URL_BASE_MERKLE", "https://base.merkle.io"), ViaProxy: true}, {Slug: "base-official", Name: "Base Official", URL: envDefault("RPC_URL_BASE_OFFICIAL", "https://mainnet.base.org")}, }, }, @@ -160,7 +154,6 @@ func chains() []Chain { {Slug: "publicnode", Name: "PublicNode", URL: envDefault("RPC_URL_BNB_PUBLICNODE", "https://bsc-rpc.publicnode.com")}, {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_BNB_DRPC", "https://bsc.drpc.org")}, {Slug: "nodies", Name: "Nodies (POKT)", URL: envDefault("RPC_URL_BNB_NODIES", "https://bsc-pokt.nodies.app")}, - {Slug: "merkle", Name: "Merkle", URL: envDefault("RPC_URL_BNB_MERKLE", "https://bsc.merkle.io"), ViaProxy: true}, {Slug: "binance", Name: "Binance Official", URL: envDefault("RPC_URL_BNB_OFFICIAL", "https://bsc-dataseed1.binance.org")}, }, }, diff --git a/harnesses/rpc-capabilities/cmd/script/probe.go b/harnesses/rpc-capabilities/cmd/script/probe.go index eb0e1308..620eee81 100644 --- a/harnesses/rpc-capabilities/cmd/script/probe.go +++ b/harnesses/rpc-capabilities/cmd/script/probe.go @@ -6,49 +6,13 @@ import ( "encoding/json" "fmt" "io" - "log" "net/http" - "net/url" - "os" "strconv" "strings" "sync" "time" ) -// probeProxyClient returns a client routing through RPC_PROBE_PROXY_URL -// (residential rotating proxy) for providers that block datacenter IPs. -// Falls back to a direct client if the env is unset, which surfaces as a -// visible 100% error rate rather than a silent measurement change. -var proxyClientOnce sync.Once -var proxyClientCached *http.Client - -func probeProxyClient() *http.Client { - proxyClientOnce.Do(func() { - raw := strings.TrimSpace(os.Getenv("RPC_PROBE_PROXY_URL")) - if raw == "" { - log.Printf("[PROBE] WARNING: ViaProxy provider configured but RPC_PROBE_PROXY_URL is unset; probing direct") - proxyClientCached = &http.Client{Timeout: probeTimeout} - return - } - u, err := url.Parse(raw) - if err != nil { - log.Printf("[PROBE] WARNING: invalid RPC_PROBE_PROXY_URL: %v; probing direct", err) - proxyClientCached = &http.Client{Timeout: probeTimeout} - return - } - proxyClientCached = &http.Client{ - Timeout: probeTimeout, - Transport: &http.Transport{ - Proxy: http.ProxyURL(u), - DisableKeepAlives: true, // fresh proxy IP per probe - MaxIdleConnsPerHost: 0, - }, - } - }) - return proxyClientCached -} - const ( // 60s (was 30s): the cluster probes ~23 chains per provider from ONE // IP per region. Keyless per-IP budgets (1RPC daily cap, dRPC CU/s) @@ -128,7 +92,7 @@ type blockHeader struct { // on cache hits rather than real RPC work. Fetching the full latest // header with a rotating request id defeats body-keyed edge caches; // the header's `number` field keeps the staleness check intact. -func callLatestBlock(ctx context.Context, url string, viaProxy bool) (block uint64, result string, latencyMs float64, err error) { +func callLatestBlock(ctx context.Context, url string) (block uint64, result string, latencyMs float64, err error) { body := []byte(fmt.Sprintf( `{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest",false],"id":%d}`, time.Now().UnixNano(), @@ -137,9 +101,6 @@ func callLatestBlock(ctx context.Context, url string, viaProxy bool) (block uint req.Header.Set("Content-Type", "application/json") req.Header.Set("User-Agent", "OpenChainBench/1.0 (+https://openchainbench.com)") client := &http.Client{Timeout: probeTimeout} - if viaProxy { - client = probeProxyClient() - } start := time.Now() resp, err := client.Do(req) @@ -214,7 +175,7 @@ func probeOne(ctx context.Context, c Chain, p Provider) { tick := func() { probeCtx, cancel := context.WithTimeout(ctx, probeTimeout) defer cancel() - block, result, latency, err := callLatestBlock(probeCtx, p.URL, p.ViaProxy) + block, result, latency, err := callLatestBlock(probeCtx, p.URL) if result == "ok" { tips.update(c.Slug, block)