Repository navigation
perf: make nested serialization 5x faster by skipping stack captures - #10
Merged
Merged
Conversation
transform() and paginate() created a RuntimeException on every call to get a useful stack trace if the resource turned out to be undefined. Capturing that stack costs a few microseconds per call, and with one call per row and per relation it dominated the CPU profile of nested responses. The error is only thrown by Item.resolve() for an undefined resource, and Collection never reads it. Item now receives the error only when the value is undefined, still created at the transform() call site so the stack keeps pointing to the calling transformer. Collection and paginate() no longer receive one. debuggingError becomes optional in the Item and Collection constructors. Item.resolve() creates the error itself when none was given. On a paginated response with 840 nested transformers, a serialization went from ~1480 µs to ~276 µs (Node 26.10, tsdown build).
RomainLanz
force-pushed
the
perf/lazy-debugging-error
branch
from
October 15, 2026 07:05
cbe8e14 to
3ab90b3
Compare
Cover nested items, items inside collections and paginators, nested collections, variants, and items created without a debugging error.
Define classes and data inside each test and assert on the stack with an inline try/catch, matching the other spec files.
Cover user -> posts -> comments -> author -> profile, where the author is missing a profile four levels down.
Member
|
Thanks 👍🏽 |
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.
transform()andpaginate()create aRuntimeExceptionon each call. This error is only for one case:Item.resolve()throws it when the resource isundefined, and its stack then points to the transformer that calledtransform(). But eachExceptionfrom@poppinss/exceptioncaptures the stack two times (theErrorconstructor, thenError.captureStackTrace), which costs ~3 µs per call. With one call per row and per relation, this was ~72% of the CPU on a paginated response with nested relations.Fix
transform()creates the error only when the value isundefined. It creates it at the same call site, so the stack still points toX.transform→Y.toObject.Collectionandpaginate()do not create an error.Collectionnever read it.debuggingErroris now optional in the exportedItemandCollectionconstructors, so existing calls continue to work. If anItemhas no error,Item.resolve()creates one when the resource isundefined. Before, this case threwTypeError: Cannot set properties of undefined.Benchmark
Node 26.10,
tsdownbuild. One copy ofbuild/before the fix and one after. Each run is a separate process, before and after interleaved, 3 rounds. Each value is the median of the 3 round medians, in µs/op (lower is better).transform()call)Verification
ProfileTransformer.transform(undefined)inUserTransformer.toObject, the stack trace is the same before and after:ProfileTransformer.transform→UserTransformer.toObject.npm run typecheckpasses.npm run lintfails with 30 Prettier errors insrc/types.ts. This file is not changed by this PR, and the same 30 errors occur on2.x. Becausenpm testruns lint first, the CI test jobs also stop before the tests run, as they do on2.x(e2d6339).Not covered by tests: the existing tests check the "Cannot transform undefined value" error message, but no test checks its stack trace or the fallback for an
Itemcreated directly without an error. I checked both with a script against the built output.