Skip to content

Refactor request metrics collection to per-call aggregation - #1390

Merged
DZakh merged 4 commits into
mainfrom
claude/prometheus-metrics-refactor-bjlbat
Jul 8, 2026
Merged

Refactor request metrics collection to per-call aggregation#1390
DZakh merged 4 commits into
mainfrom
claude/prometheus-metrics-refactor-bjlbat

Conversation

@DZakh

@DZakh DZakh commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

Refactor RPC/HyperSync request metrics collection from direct Prometheus increments to per-call aggregation. Instead of recording metrics immediately at each request site, sources now accumulate request statistics (method name and duration) and return them alongside query results. SourceManager aggregates these per (source, method) and exposes them via a new Metrics endpoint.

Key Changes

  • Source interface updates: getHeightOrThrow() and getBlockHashes() now return wrapper types (getHeightResponse, getBlockHashesResponse) containing both the result and an array of requestStat entries (method name + seconds).

  • Request tracking in RpcSource: Replaced direct Prometheus.SourceRequestCount.increment() calls with a recordRequest callback that accumulates stats in a pendingRequestStats array. Stats are drained and returned with each query result via drainRequestStats().

  • SourceManager aggregation: Added requestStats: dict<requestStatAgg> to track cumulative per-method counts and durations. New recordRequestStats() function merges incoming stats arrays into this dict. New getRequestStatSamples() exports flattened (source, method) aggregates.

  • Metrics rendering: New Metrics.renderSourceRequests() function converts SourceManager samples into Prometheus text format for envio_source_request_total and envio_source_request_seconds_total metrics.

  • HyperSync/HyperFuel sources: Updated to return requestStats arrays from getItemsOrThrow() and getHeightOrThrow(). Request timing now captured at query boundaries rather than at Prometheus call sites.

  • Test/mock updates: Updated MockIndexer, SimulateSource, and test helpers to return the new wrapper types with empty requestStats arrays.

Implementation Details

  • Request stats are accumulated in a shared pendingRequestStats array within each source's make() function, allowing concurrent calls to share the same recording mechanism without attribution issues.
  • Each source method drains pending stats when returning, ensuring per-source totals remain exact even with concurrent in-flight calls.
  • Prometheus direct calls (Prometheus.SourceRequestCount.increment/addSeconds) are removed from request paths; the new metrics are rendered by Metrics module from SourceManager's aggregates.

https://claude.ai/code/session_019LHZ2vfkopb9LwS8zTdQ8F

Summary by CodeRabbit

  • New Features
    • Added request timing metrics for source operations by rendering per-source, per-chain, per-method request count and duration samples.
    • Source operations now collect per-request timing and include it in the data returned from block/height lookups and item fetching, enabling richer metrics output.
  • Bug Fixes
    • Improved metric consistency across RPC, HyperSync, HyperFuel, SVM, and simulation flows by capturing local timings instead of directly updating Prometheus counters, including during retries and multi-step queries.
  • Tests
    • Updated mocks and assertions to match the new structured timing fields and the renderSourceRequests output behavior.

Sources no longer call Prometheus directly for per-request tracking (getLogs,
getHeight, getBlockHashes, RPC's eth_* calls, SVM getInstructions). Each method
now returns the (method, seconds) stats for the actual backend requests it
made; SourceManager aggregates them per source, and Metrics inlines the
aggregated counts/seconds into the existing envio_source_request_total /
envio_source_request_seconds_total series alongside prom-client's registry.
RPC's shared block/transaction/receipt loaders record through a per-source
pending buffer at the loaderFn boundary, so only actual network requests count
(cache/dedup hits don't). SVM HyperSync now tracks seconds for getInstructions
and getBlockHashes, matching the EVM HyperSync source. Height-stream sources
(HyperSync SSE, RPC WebSocket) are unchanged, still on Prometheus.SourceRequestCount.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 27cf28f5-5cd5-4d54-834c-9a65650511e5

📥 Commits

Reviewing files that changed from the base of the PR and between 4fb61c3 and 15e5d9e.

📒 Files selected for processing (2)
  • packages/envio/src/Metrics.res
  • scenarios/test_codegen/test/lib_tests/Metrics_test.res
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/envio/src/Metrics.res

📝 Walkthrough

Walkthrough

This PR replaces direct Prometheus request instrumentation with per-request stats objects. Sources now return requestStats, SourceManager aggregates them, and Metrics renders the collected samples as Prometheus text. Tests and mocks were updated for the new structured return shapes.

Changes

Request stats plumbing

Layer / File(s) Summary
Source contract and types for request stats
packages/envio/src/sources/Source.res, packages/envio/src/sources/SourceManager.resi, packages/envio/src/Prometheus.res
Adds request-stat types and response wrappers, updates public source contracts, and removes the old request-count metric module.
HyperSync block-data timing
packages/envio/src/sources/HyperSync.res, packages/envio/src/sources/HyperSync.resi
Threads requestStats through HyperSync block-data queries and returns them alongside query results.
SourceManager aggregation and metrics rendering
packages/envio/src/sources/SourceManager.res, packages/envio/src/Metrics.res
Adds request-stat aggregation, sample extraction, recording hooks, and Prometheus rendering for the collected request metrics.
Source requestStats propagation
packages/envio/src/sources/HyperFuelSource.res, packages/envio/src/sources/HyperSyncSource.res, packages/envio/src/sources/RpcSource.res, packages/envio/src/sources/Svm.res, packages/envio/src/sources/SvmHyperSyncSource.res, packages/envio/src/sources/SimulateSource.res, packages/envio/src/sources/HyperSyncHeightStream.res, packages/envio/src/sources/RpcWebSocketHeightStream.res
Updates source implementations and streams to collect requestStats, return structured results, and stop emitting direct Prometheus request counters.
Tests and mocks updated for structured return shapes
scenarios/fuel_test/test/HyperFuelHeight_test.res, scenarios/test_codegen/test/RateLimit_test.res, scenarios/test_codegen/test/RpcSource_test.res, scenarios/test_codegen/test/helpers/MockIndexer.res, scenarios/test_codegen/test/rollback/ChainMocking.res, scenarios/test_codegen/test/lib_tests/Metrics_test.res
Updates tests and mock helpers to destructure structured height/result payloads, include empty requestStats arrays, and verify the new metrics renderer output.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Source
  participant SourceManager
  participant Metrics
  Source->>SourceManager: return {height/result, requestStats}
  SourceManager->>SourceManager: recordRequestStats(sourceState, requestStats)
  Metrics->>SourceManager: getRequestStatSamples()
  Metrics->>Metrics: renderSourceRequests(samples)
Loading

Possibly related PRs

  • enviodev/hyperindex#1272: Both PRs modify packages/envio/src/sources/HyperSyncSource.res in the getHeightOrThrow path.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: request metrics are now collected per call and aggregated later.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

…rics-refactor-bjlbat

# Conflicts:
#	packages/envio/src/sources/SvmHyperSyncSource.res
… text

Height-stream sources (HyperSync SSE, RPC WebSocket) were the last callers of
Prometheus.SourceRequestCount, splitting source-request tracking across
prom-client and our own SourceManager-aggregated state. Move them onto the
latter: SourceManager now records a count-only "heightSubscription" stat
(no seconds — there's no request/response to time) at the point it creates a
height subscription, rather than the streams recording per received message.

Delete Prometheus.SourceRequestCount and its label schema, and inline the
envio_source_request_total/envio_source_request_seconds_total HELP/TYPE lines
directly in Metrics.renderSourceRequests instead of relying on prom-client's
registry for them. renderSourceRequests now takes a flat samples array rather
than chainStates, making it directly testable; add coverage for the rendered
text with and without samples.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/envio/src/sources/RpcSource.res (1)

316-331: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Failed requests silently skip recordRequest, undercounting error-path metrics.

queryLogs (eth_getLogs), the transaction loader (eth_getTransactionByHash), and the receipt loader (eth_getTransactionReceipt) all record via Promise.thenResolve on the success path only. If the underlying request rejects, recordRequest is never called for that attempt, and the rejection just propagates.

This contradicts the invariant documented a few lines below (1040-1047): "Every actual request (cache/dedup hits never reach recordRequest) pushes here." getKnownRawBlockWithBackoff (lines 58-72) gets this right — it records timing on both the exception and success branches. The three sites here don't, so RPC errors (rate limits, oversized-response errors, transient failures) systematically undercount envio_source_request_* metrics precisely during the periods operators most need visibility into.

🐛 Proposed fix (mirroring the block-loader's pattern)
   let queryLogs = ({addresses, topicQuery}: logSelection) => {
     let timerRef = Performance.now()
     rpcClient.getLogs({
       fromBlock,
       toBlock,
       ?addresses,
       topics: topicQuery->Array.map(filter =>
         switch filter {
         | Rpc.GetLogs.Null => Nullable.null
         | Single(topic) => Nullable.make([topic])
         | Multiple(topics) => Nullable.make(topics)
         }
       ),
     })->Promise.thenResolve(items => {
       recordRequest(~method="eth_getLogs", ~seconds=timerRef->Performance.secondsSince)
       items
     })
+    ->Promise.catch(exn => {
+      recordRequest(~method="eth_getLogs", ~seconds=timerRef->Performance.secondsSince)
+      exn->Promise.reject
+    })
   }
       ~loaderFn=transactionHash => {
         let timerRef = Performance.now()
         Rpc.GetTransactionByHash.rawRoute
         ->Rest.fetch(transactionHash, ~client)
         ->Promise.thenResolve(res => {
           recordRequest(
             ~method="eth_getTransactionByHash",
             ~seconds=timerRef->Performance.secondsSince,
           )
           res
         })
+        ->Promise.catch(exn => {
+          recordRequest(
+            ~method="eth_getTransactionByHash",
+            ~seconds=timerRef->Performance.secondsSince,
+          )
+          exn->Promise.reject
+        })
       },

Apply the analogous change to the receipt loader (eth_getTransactionReceipt).

As per coding guidelines, "Use try/catch as expressions instead of refs for tracking success/failure" — an async/switch await ... { exception ... } rewrite (matching getKnownRawBlockWithBackoff's style) is a cleaner alternative to the Promise.catch chain above.

Also applies to: 1061-1070, 1121-1130

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/sources/RpcSource.res` around lines 316 - 331, The RPC
loaders in RpcSource.res only call recordRequest on the success path, so
rejected requests for queryLogs, eth_getTransactionByHash, and
eth_getTransactionReceipt are never counted. Update these loaders to record
timing on both success and failure, using the same try/catch-as-expression
pattern already used by getKnownRawBlockWithBackoff so recordRequest is always
invoked before rethrowing the error. Keep the existing method names in the
recordRequest calls and apply the same fix consistently across the three
affected request handlers.

Source: Coding guidelines

🧹 Nitpick comments (6)
packages/envio/src/sources/HyperFuelSource.res (1)

484-499: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor: inconsistent qualification of method field between the two requestStat literals.

Line 306 writes {Source.method: "getLogs", seconds: pageFetchTime} while line 498 writes {method: "getHeight", seconds} unqualified. Both compile fine given open Source at the top of the file, but the inconsistency is a small readability nit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/sources/HyperFuelSource.res` around lines 484 - 499, The
requestStats literal in getHeightOrThrow uses an unqualified method field while
the other requestStat entry in this file uses Source.method, creating a small
inconsistency. Update the getHeightOrThrow record in HyperFuelSource so the
method field is qualified the same way as the getLogs entry, keeping the style
consistent across requestStat literals.
packages/envio/src/sources/SourceManager.res (2)

3-6: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the "rendered by Metrics.renderSourceRequests" pointer from the comment.

The first sentence restates the type's obvious purpose; the last sentence points to a specific downstream caller, both disallowed by the .res comment guideline.

✏️ Suggested trim
-// Cumulative per-method request count/time for a source, aggregated from the
-// requestStat arrays returned by its methods. Rendered into
-// envio_source_request_* by Metrics.renderSourceRequests.
+// Cumulative count/total-seconds for one (source, method) pair.
 type requestStatAgg = {mutable count: int, mutable seconds: float}

As per coding guidelines, **/*.res: "Don't write a comment that restates what the code already says... which callers use a value... pointers to where something is 'now defined'".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/sources/SourceManager.res` around lines 3 - 6, Remove the
downstream caller pointer from the `requestStatAgg` comment in
`SourceManager.res` and trim it to only the necessary non-obvious note; keep the
type definition itself unchanged and update the surrounding comment so it no
longer restates the obvious purpose or mentions `Metrics.renderSourceRequests`.

Source: Coding guidelines


31-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Same caller-pointer comment issue as the requestStatAgg type.

"for Metrics.renderSourceRequests to inline into the /metrics response" names a specific caller, which the .res guideline says to avoid.

✏️ Suggested trim
-// Flattened (source, method) aggregates for Metrics.renderSourceRequests to
-// inline into the /metrics response.
+// Flattened (source, chain, method) view of the per-source request aggregates.
 type requestStatSample = {

As per coding guidelines, **/*.res: "Don't write a comment that restates what the code already says... which callers use a value...".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/sources/SourceManager.res` around lines 31 - 39, The
comment on requestStatSample repeats implementation intent and names a specific
caller, which should be removed to follow the .res comment guidelines. Trim or
delete the caller-pointer wording in SourceManager.res and keep only a minimal
description of the type’s purpose, alongside requestStatAgg if needed, so the
definition stands without referencing Metrics.renderSourceRequests or /metrics.

Source: Coding guidelines

packages/envio/src/sources/Source.res (1)

10-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Trim the comment to the non-obvious part; drop the "who consumes this" pointer.

The first two lines capture a genuinely non-obvious invariant (cache/dedup hits aren't requests) — keep those. The last sentence ("SourceManager aggregates these per (source, method) into the envio_source_request_* metrics") describes a downstream caller/consumer, which the guideline explicitly disallows.

✏️ Suggested trim
-// A single backend request a source method actually made (cache/dedup hits
-// aren't requests), with the time it took. SourceManager aggregates these
-// per (source, method) into the envio_source_request_* metrics.
+// A single backend request a source method actually made (cache/dedup hits
+// aren't requests), with the time it took.
 type requestStat = {method: string, seconds: float}

As per coding guidelines, **/*.res: "Don't write a comment that restates what the code already says — module purpose, what a function does, which callers use a value, history of a refactor, or pointers to where something is 'now defined'".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/sources/Source.res` around lines 10 - 13, Trim the
documentation comment above requestStat in Source.res to keep only the
non-obvious invariant that cache/dedup hits are not counted as requests, and
remove the downstream usage note about SourceManager aggregating into
envio_source_request_* metrics. Keep the comment focused on the meaning of
requestStat itself and use requestStat as the anchor when locating the block.

Source: Coding guidelines

packages/envio/src/Metrics.res (1)

21-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Trim the comment to just the HELP/TYPE invariant; drop the restated purpose.

The last sentence (about the metric name already being declared via Prometheus.SourceRequestCount) is a genuinely non-obvious constraint worth keeping. The first two sentences just restate what the function computes and where the data comes from.

✏️ Suggested trim
-// Samples for envio_source_request_total/envio_source_request_seconds_total,
-// aggregated per (source, chain, method) by SourceManager. The HELP/TYPE lines
-// for both metric names are already emitted by prom-client's registry, via
-// Prometheus.SourceRequestCount — still used directly by height-stream sources.
+// HELP/TYPE lines for envio_source_request_total/envio_source_request_seconds_total
+// are already emitted by prom-client's registry via Prometheus.SourceRequestCount
+// (still used directly by height-stream sources), so we must not repeat them here.
 let renderSourceRequests = (~chainStates: dict<ChainState.t>) => {

As per coding guidelines, **/*.res: "Don't write a comment that restates what the code already says — module purpose, what a function does...".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/Metrics.res` around lines 21 - 24, Trim the comment in
Metrics.res so it only preserves the non-obvious HELP/TYPE invariant tied to
Prometheus.SourceRequestCount; remove the sentences that restate what
SourceManager aggregates or how the samples are produced, and keep only the note
that both metric names already have HELP/TYPE lines emitted by the registry and
are still used directly by height-stream sources.

Source: Coding guidelines

scenarios/test_codegen/test/RpcSource_test.res (1)

39-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider asserting on requestStats too.

The test now receives requestStats from getHeightOrThrow() but only destructures/asserts height. Since this is the new feature under test in this PR, asserting the array contains an eth_blockNumber entry with a non-negative seconds value would give real coverage of the new contract, not just height parsing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scenarios/test_codegen/test/RpcSource_test.res` around lines 39 - 58, The
RpcSource.getHeightOrThrow test currently only verifies height, so it does not
cover the new requestStats contract. Update the test in RpcSource_test.res to
destructure requestStats alongside height from getHeightOrThrow, and add an
assertion that requestStats includes an eth_blockNumber entry with a
non-negative seconds value. Use the RpcSource.make and getHeightOrThrow symbols
to locate the test and keep the existing height assertions intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/envio/src/Metrics.res`:
- Around line 21-53: The enviro_source_request_* metrics are being emitted twice
in the same scrape because collect() appends renderSourceRequests(~chainStates)
even though PromClient.defaultRegister->PromClient.metrics already includes
Prometheus.SourceRequestCount. Update collect and/or renderSourceRequests so the
SourceManager samples are only exposed once per scrape, or rename the
scrape-time aggregate to distinct metric names; use the existing collect and
renderSourceRequests symbols to locate the duplicate emission.

In `@packages/envio/src/sources/SvmHyperSyncSource.res`:
- Around line 632-645: The catch path in SvmHyperSyncSource.getBlockHashes is
discarding already collected requestStats when queryBlockDataRange throws after
partial success. Update the error branch to return the accumulated requestStats
from the successful pages instead of an empty array, matching the RpcSource
pattern and preserving buffered stats on partial failures.

---

Outside diff comments:
In `@packages/envio/src/sources/RpcSource.res`:
- Around line 316-331: The RPC loaders in RpcSource.res only call recordRequest
on the success path, so rejected requests for queryLogs,
eth_getTransactionByHash, and eth_getTransactionReceipt are never counted.
Update these loaders to record timing on both success and failure, using the
same try/catch-as-expression pattern already used by getKnownRawBlockWithBackoff
so recordRequest is always invoked before rethrowing the error. Keep the
existing method names in the recordRequest calls and apply the same fix
consistently across the three affected request handlers.

---

Nitpick comments:
In `@packages/envio/src/Metrics.res`:
- Around line 21-24: Trim the comment in Metrics.res so it only preserves the
non-obvious HELP/TYPE invariant tied to Prometheus.SourceRequestCount; remove
the sentences that restate what SourceManager aggregates or how the samples are
produced, and keep only the note that both metric names already have HELP/TYPE
lines emitted by the registry and are still used directly by height-stream
sources.

In `@packages/envio/src/sources/HyperFuelSource.res`:
- Around line 484-499: The requestStats literal in getHeightOrThrow uses an
unqualified method field while the other requestStat entry in this file uses
Source.method, creating a small inconsistency. Update the getHeightOrThrow
record in HyperFuelSource so the method field is qualified the same way as the
getLogs entry, keeping the style consistent across requestStat literals.

In `@packages/envio/src/sources/Source.res`:
- Around line 10-13: Trim the documentation comment above requestStat in
Source.res to keep only the non-obvious invariant that cache/dedup hits are not
counted as requests, and remove the downstream usage note about SourceManager
aggregating into envio_source_request_* metrics. Keep the comment focused on the
meaning of requestStat itself and use requestStat as the anchor when locating
the block.

In `@packages/envio/src/sources/SourceManager.res`:
- Around line 3-6: Remove the downstream caller pointer from the
`requestStatAgg` comment in `SourceManager.res` and trim it to only the
necessary non-obvious note; keep the type definition itself unchanged and update
the surrounding comment so it no longer restates the obvious purpose or mentions
`Metrics.renderSourceRequests`.
- Around line 31-39: The comment on requestStatSample repeats implementation
intent and names a specific caller, which should be removed to follow the .res
comment guidelines. Trim or delete the caller-pointer wording in
SourceManager.res and keep only a minimal description of the type’s purpose,
alongside requestStatAgg if needed, so the definition stands without referencing
Metrics.renderSourceRequests or /metrics.

In `@scenarios/test_codegen/test/RpcSource_test.res`:
- Around line 39-58: The RpcSource.getHeightOrThrow test currently only verifies
height, so it does not cover the new requestStats contract. Update the test in
RpcSource_test.res to destructure requestStats alongside height from
getHeightOrThrow, and add an assertion that requestStats includes an
eth_blockNumber entry with a non-negative seconds value. Use the RpcSource.make
and getHeightOrThrow symbols to locate the test and keep the existing height
assertions intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1b02fd05-8656-4421-ade9-6f60292152d6

📥 Commits

Reviewing files that changed from the base of the PR and between 064e653 and 57610d8.

📒 Files selected for processing (17)
  • packages/envio/src/Metrics.res
  • packages/envio/src/sources/HyperFuelSource.res
  • packages/envio/src/sources/HyperSync.res
  • packages/envio/src/sources/HyperSync.resi
  • packages/envio/src/sources/HyperSyncSource.res
  • packages/envio/src/sources/RpcSource.res
  • packages/envio/src/sources/SimulateSource.res
  • packages/envio/src/sources/Source.res
  • packages/envio/src/sources/SourceManager.res
  • packages/envio/src/sources/SourceManager.resi
  • packages/envio/src/sources/Svm.res
  • packages/envio/src/sources/SvmHyperSyncSource.res
  • scenarios/fuel_test/test/HyperFuelHeight_test.res
  • scenarios/test_codegen/test/RateLimit_test.res
  • scenarios/test_codegen/test/RpcSource_test.res
  • scenarios/test_codegen/test/helpers/MockIndexer.res
  • scenarios/test_codegen/test/rollback/ChainMocking.res

Comment thread packages/envio/src/Metrics.res Outdated
Comment on lines +632 to 645
let (blockDatas, requestStats) = await queryBlockDataRange(
~fromSlot=minSlot.contents,
~toSlot=maxSlot.contents,
)
// Keep one entry per requested slot; drop duplicates and unrelated slots.
Ok(blockDatas->Array.filter(data => requested->Utils.Set.delete(data.blockNumber)))
{
Source.result: Ok(
blockDatas->Array.filter(data => requested->Utils.Set.delete(data.blockNumber)),
),
requestStats,
}
} catch {
| exn => Error(exn)
| exn => {Source.result: Error(exn), requestStats: []}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Compare failure-path stat handling: RpcSource drains buffered stats; check how SVM differs.
rg -nP -C4 'drainRequestStats|pendingRequestStats' packages/envio/src/sources/RpcSource.res
rg -nP -C3 'requestStats: \[\]|catch' packages/envio/src/sources/SvmHyperSyncSource.res

Repository: enviodev/hyperindex

Length of output: 2921


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the surrounding implementation of the failing path and the RPC drain pattern.
sed -n '600,660p' packages/envio/src/sources/SvmHyperSyncSource.res
printf '\n--- RPC drain path ---\n'
sed -n '1040,1060p' packages/envio/src/sources/RpcSource.res
printf '\n--- RPC getBlockHashes / catch path ---\n'
sed -n '1368,1452p' packages/envio/src/sources/RpcSource.res

Repository: enviodev/hyperindex

Length of output: 5603


Preserve buffered request stats on getBlockHashes failures

If queryBlockDataRange throws after some pages succeed, getBlockHashes returns requestStats: [], dropping the requests that already ran. That makes this source go silent during partial failures; return the accumulated stats from the catch path instead, like RpcSource does.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/sources/SvmHyperSyncSource.res` around lines 632 - 645,
The catch path in SvmHyperSyncSource.getBlockHashes is discarding already
collected requestStats when queryBlockDataRange throws after partial success.
Update the error branch to return the accumulated requestStats from the
successful pages instead of an empty array, matching the RpcSource pattern and
preserving buffered stats on partial failures.

…uest text

heightSubscription never carries real timing, so its seconds line was a
misleading always-zero value; drop it. Drop both HELP/TYPE blocks entirely
when there are no samples to report at all.
@DZakh
DZakh merged commit 5d8eeaa into main Jul 8, 2026
8 checks passed
@DZakh
DZakh deleted the claude/prometheus-metrics-refactor-bjlbat branch July 8, 2026 12:30
DZakh pushed a commit that referenced this pull request Jul 8, 2026
…get-redesign-yl7t1u

Brings in the block-store/ChainState rework (#1369) and per-call metrics
aggregation (#1390). Auto-merged cleanly; verified envio + test_codegen
compile and the scheduler, rollback, SourceManager, and IndexerState tests
pass against the merged tree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174BGgup49cCEGmFyBW7Wyx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants