Skip to content

perf(common): stop serializing every SDK request behind a 100ms poll - #14687

Merged
dylanjeffers merged 1 commit into
mainfrom
design/artwork-load-time
Oct 19, 2026
Merged

dylanjeffers merged 1 commit into
mainfrom
design/artwork-load-time

Conversation

@dylanjeffers

Copy link
Copy Markdown
Contributor

Why

Every SDK request on web goes through addRequestSignatureMiddleware, which (inside a mutex) calls audiusWalletClient.getAddresses() → getAccount() → hedgehog.waitUntilReady(). The common Hedgehog port's waitUntil polls with setInterval(…, 100) and never checks the condition before the first tick, so an already-ready wait still costs a full 100 ms. Combined with the mutex, every API request starts ~100 ms after the previous one was issued, in issue order, even when the wallet has been ready since boot.

On page load that queue is 8–12 requests logged out and 20+ logged in (account, notifications, comms, chats…), so page data such as Explore's shelves or the track page's lineup starts 1–2 s later than it could, and artwork, which is requested right after the data lands, inherits that delay. This is the dominant client-side contributor to "artwork still skeleton" on the pages reviewed for the design refresh. The same waitUntil existed in @audius/hedgehog before #14662, so this is not a regression from the viem port.

What

waitUntil now returns a resolved promise when the condition already holds; the 100 ms poll is only used while the wallet is still being restored. Two unit tests cover both paths. Mobile uses the same helper and gets the same benefit.

Measurements

Headless Chrome (Playwright, 1440x900, logged out, cold cache, dev server with prod data, median of 3 runs, ms from navigation start). The built-in browser pane was not usable for this: when the pane is hidden the page gets no frames, idle callbacks only fire at their timeout, and lazy loading cannot trigger, which produced 10 s "stalls" that do not exist in a visible tab.

page API start gap (median) first data request last page-data request start first artwork painted 10 artworks painted
/explore before 102 ms 3672 4385 5011 6132
after 0 ms 3486 3500 3955 4437
/trending before 115 ms 2438 2438 3118 3910
after 0 ms 2254 2254 3229 4009
/radiojuicy/tohaj-long-drive before 120 ms 2354 4281 3409 4716
after 83 ms 2838 4094 4111 4820
/radiojuicy before 162 ms 2511 3836 3717 4117
after 17 ms 2410 3733 3912 4412

Logged out, only Explore has page data deep enough in the queue (requests 8–10) to show the win: its shelf data starts 0.9 s earlier and its artwork paints 1.0–1.7 s earlier. Trending, track and profile issue their page data first, so they are unchanged within dev-server noise (the track page's remaining 83 ms gap is its own dependent chain: stems, comments and remixes are issued after the track response). The track page's "after" runs also had a slower DOMContentLoaded (1.0–2.0 s vs 0.9 s) from Vite transforms, which shifts every column; relative to DOMContentLoaded they match.

Logged in, every page benefits: the ~20 account, notification and comms requests the shell issues at boot were ahead of page data in the same queue (in the pane, logged in, Trending's data request started 0.4 s after the first request and Explore's shelf data had not started 5 s in).

Request start gap = median spacing between consecutive API request starts (the serialization signature). "First art painted" = first <img> from a content node with complete && naturalWidth > 0.

Artwork requests themselves were never the problem: on all four pages the first content-node image request starts 30–200 ms after the data response, sizes are correct for the slot (150x150 tiles, 480x480 Explore cards, 1000x1000 hero only), and Explore's unloaded images are offscreen carousel tiles with loading="lazy".

🤖 Generated with Claude Code

hedgehogLib's waitUntil polled on a 100ms interval without checking the
condition first, so hedgehog.waitUntilReady() cost a full tick even once
the wallet was restored. The SDK's request-signature middleware calls it
for every request inside a mutex, which spaced all API requests ~100ms
apart in issue order and pushed page data (and the artwork requested
after it) behind the shell's account/notification/comms calls.

Resolve immediately when the condition already holds.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 19, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0701a9a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@dylanjeffers
dylanjeffers enabled auto-merge (squash) October 19, 2026 22:45
@dylanjeffers
dylanjeffers merged commit c3a4edf into main Oct 19, 2026
8 checks passed
@dylanjeffers
dylanjeffers deleted the design/artwork-load-time branch October 19, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant