Stop the main thread freezing for seconds after login - #329
Merged
Conversation
Logging in froze the app for 4-6 seconds with the nav bar already on screen. Profiling it (new harness: scripts/perf/login-repro.mjs) shows the wait tracks the CPU throttle, not pod latency: it is one long task, not a sync waiting on the network. The task is @inrupt/solid-client's fromRdfJsDataset. It accumulates the parsed quads immutably, spreading the whole graph object to add each subject, so it costs O(quads x subjects). The question set is the one document that grows on both axes at once — every person, question, option and item lives in it — and at 5,182 quads over 1,085 subjects that single conversion took 1,044ms unthrottled and 4.6s on a mid-range phone. The Turtle parse it follows takes 25ms. So build the same structure in one linear pass instead (rdfDataset.ts, 9ms for the same document; rdfDataset.test.ts asserts it equals fromRdfJsDataset's own output for the app's real documents), and split the fetch from the parse in loadMultipleRdfFromPod so a container's files still download in parallel but their parses take a yield between them rather than draining back to back in one task. Same pod, same throttle, before -> after: longest frozen frame 6,492ms -> 281ms, total blocking time 6,799ms -> 518ms, lists on screen 11,665ms -> 5,461ms. Owning the parse also fixes the conditional-GET cache, which never fired against a real pod: solid-client cannot construct its FetchError for a 304, so the error loadRdfFromPod caught carried no statusCode to match on and the load failed instead of returning the cached result. The test covering it passed because it mocked the error solid-client would have thrown rather than the one it does; it now drives a real 304 Response through the real path. Findings and numbers: docs/login-performance.md. Also fixes two things this ran into: the solid-dev skill's start.sh picked up two URLs from the CSS controls JSON and so never created its pod, and the perf harnesses' shared login helper was stale against the current provider picker. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EAwB4CtuLyPZvRnwAyayCP
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Logging in froze the app for 4-6 seconds with the nav bar already on
screen. Profiling it (new harness: scripts/perf/login-repro.mjs) shows the
wait tracks the CPU throttle, not pod latency: it is one long task, not a
sync waiting on the network.
The task is @inrupt/solid-client's fromRdfJsDataset. It accumulates the
parsed quads immutably, spreading the whole graph object to add each
subject, so it costs O(quads x subjects). The question set is the one
document that grows on both axes at once — every person, question, option
and item lives in it — and at 5,182 quads over 1,085 subjects that single
conversion took 1,044ms unthrottled and 4.6s on a mid-range phone. The
Turtle parse it follows takes 25ms.
So build the same structure in one linear pass instead (rdfDataset.ts,
9ms for the same document; rdfDataset.test.ts asserts it equals
fromRdfJsDataset's own output for the app's real documents), and split
the fetch from the parse in loadMultipleRdfFromPod so a container's files
still download in parallel but their parses take a yield between them
rather than draining back to back in one task.
Same pod, same throttle, before -> after: longest frozen frame 6,492ms ->
281ms, total blocking time 6,799ms -> 518ms, lists on screen 11,665ms ->
5,461ms.
Owning the parse also fixes the conditional-GET cache, which never fired
against a real pod: solid-client cannot construct its FetchError for a
304, so the error loadRdfFromPod caught carried no statusCode to match on
and the load failed instead of returning the cached result. The test
covering it passed because it mocked the error solid-client would have
thrown rather than the one it does; it now drives a real 304 Response
through the real path.
Findings and numbers: docs/login-performance.md.
Also fixes two things this ran into: the solid-dev skill's start.sh picked
up two URLs from the CSS controls JSON and so never created its pod, and
the perf harnesses' shared login helper was stale against the current
provider picker.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01EAwB4CtuLyPZvRnwAyayCP