feat(registry): fetch project data, limits and features concurrently - #35
Open
geekbrother wants to merge 1 commit into
Open
feat(registry): fetch project data, limits and features concurrently#35geekbrother wants to merge 1 commit into
geekbrother wants to merge 1 commit into
Conversation
`project_data_with` awaited its upstream calls in sequence, so a single call cost the sum of two round trips (`include_limits`, which is nearly every caller) or three (`include_limits` + `include_features`). The calls are independent and span two hosts — the explorer serves project data, the internal API serves limits and features — so there was never a reason to chain them. Issue all three concurrently with `tokio::join!`. Wall time becomes the slowest single call instead of their sum, which matters because callers pay this on every cache miss. `join!` rather than `try_join!`, deliberately: `try_join!` returns the first error it sees, so a transport or 5xx failure on limits/features would pre-empt a 404 on the base project data and report "registry unavailable" where the sequential code reported "project not found". Callers fail open on a registry error, so that regression would admit unregistered projects — the bug #33 and #34 fixed. Awaiting all three and then resolving them in the original precedence order keeps the outcome identical to the sequential version for every input; only timing changes. The one behavioural trade: a project that 404s now also issues the limits/features calls the sequential version skipped. Unknown project IDs are a negligible share of traffic and callers cache the not-found result, so this buys a latency win on the common path for a rare extra call. Both new tests were verified to fail without the change: - the concurrency test measures 928ms against sequential code and ~300ms concurrent, with the bound at 600ms - the not-found test reports `ServerError` under a `try_join!` implementation, and passes with `join!` Adds the `macros` tokio feature for `join!`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
geekbrother
marked this pull request as ready for review
July 31, 2026 09:25
xDarksome
approved these changes
Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
project_data_withawaited its upstream calls in sequence, so one call cost the sum of two round trips (include_limits— nearly every caller) or three (include_limits+include_features). The calls are independent and span two different hosts — the explorer serves/internal/project/key/{id}, the internal API serves/internal/v1/project-limitsand/appkit/v1/config— so there was never a reason to chain them.This issues all three concurrently with
tokio::join!. Wall time becomes the slowest single call rather than their sum.Context: blockchain-api pays this on every cache miss, and its registry fetch latency sits at ~200ms baseline with spikes to the 800ms client timeout. Serialised round trips are a large part of that, and the additive tail means the combined p99 is worse than either upstream's.
Why
join!and nottry_join!try_join!returns the first error it sees. A transport or 5xx failure on limits/features would then pre-empt a 404 on the base project data and report "registry unavailable" where the sequential code reported "project not found". Callers fail open on a registry error, so that would admit unregistered projects — the fail-open bug #33 and #34 fixed.Awaiting all three and resolving them in the original precedence order keeps the outcome identical to the sequential version for every input; only timing changes.
This isn't hypothetical — I built the
try_join!variant and ran the new test against it:Trade-off
A project that 404s now also issues the limits/features calls the sequential version skipped. Unknown project IDs are a negligible share of traffic and callers cache the not-found result, so this trades a rare extra call for a latency win on the common path. Documented in the code comment.
Test plan
Two tests added, both verified to fail without the change:
project_data_with_issues_upstream_calls_concurrently— three endpoints each delayed 300ms; asserts the call completes in <600ms. Measured 928ms against sequential code, ~300ms concurrent.project_data_with_reports_not_found_even_when_sub_resource_fails— base 404 + limits 500 must yieldOk(None). Fails withServerErrorundertry_join!, passes withjoin!.cargo clippy --all-features --all-targetsclean;cargo fmttouched only the changed regions.Adds the
macrostokio feature forjoin!.Downstream note
blockchain-api pins
cerberusatv0.16.1, so this needs a new tag before it can be picked up there. Left as a release decision rather than assumed.🤖 Generated with Claude Code