Skip to content

shared frozen constant for empty JSON bodies#51

Merged
VikramAditya33 merged 1 commit into
FOSSFORGE:mainfrom
aryan55254:fix/json-gc-pressure
Apr 27, 2026
Merged

shared frozen constant for empty JSON bodies#51
VikramAditya33 merged 1 commit into
FOSSFORGE:mainfrom
aryan55254:fix/json-gc-pressure

Conversation

@aryan55254
Copy link
Copy Markdown
Contributor

@aryan55254 aryan55254 commented Apr 27, 2026

Fixes [#34]

Overview

This PR introduces a module-level constant EMPTY_FROZEN_OBJECT defined once at module load time, eliminating redundant object allocations for empty request bodies.

Implementation

/**
 * This is a single shared constant used inside the request class.
 * The object is created and frozen exactly once when the file is first loaded. 
 * Subsequent requests just point to this same memory address.
 */
const EMPTY_FROZEN_OBJECT = Object.freeze({});
 
// Later, inside the json() method:
this.cachedJson = EMPTY_FROZEN_OBJECT as T; // zero allocation

Why This Matters

Metric Inline Object.freeze({}) Module-level Constant
Allocations 1 per qualifying request 1 total (at module load)
GC Pressure Proportional to request rate Zero
Referential Equality (===) ❌ Each call returns a different object ✅ Always the same object

Performance & Validation

Memory Footprint

  • Local benchmark with --logHeapUsage showed 6 MB reduction in peak heap size
    • Before: 48 MB
    • After: 42 MB

Identity Verification

  • Added regression test in request.spec.ts using .toBe() assertion
  • Ensures multiple distinct requests share the exact same memory reference for empty bodies
  • ✅ Test passes with this optimization (previously failed on main)

Checklist

  • Read relevant documentation at uwest.js.org/docs
  • Linter passes locally with no errors or warnings
  • Written tests for changes (Identity/Referential equality check)
  • All existing and new tests pass (95 total)
  • Branch rebased on latest main
  • Commits squashed into clean, logical units

Summary by CodeRabbit

  • Tests

    • Added test coverage for request body parsing caching behavior.
  • Refactor

    • Optimized empty request body handling by reusing cached frozen objects instead of creating new instances for GET, HEAD, and DELETE requests.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0f1caa81-c36e-467d-8aeb-54f1a29f317a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)

❌ Error creating Unit Test PR.

  • Create PR with unit tests
  • Commit unit tests in branch fix/json-gc-pressure

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

- Replaces inline Object.freeze({}) with a module-level constant to eliminate redundant allocations.
- Reduces heap usage by ~6MB during core test suites.
- Adds a regression test to verify referential identity (zero-allocation).
@aryan55254 aryan55254 force-pushed the fix/json-gc-pressure branch from c922451 to 528c197 Compare April 27, 2026 15:43
@FOSSFORGE FOSSFORGE deleted a comment from coderabbitai Bot Apr 27, 2026
@FOSSFORGE FOSSFORGE deleted a comment from coderabbitai Bot Apr 27, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Agent ran but didn't generate any test files. Tests may already exist or changes don't require additional tests.

@aryan55254
Copy link
Copy Markdown
Contributor Author

Hey @VikramAditya33 have a look at the pr , all the checks have passed
I dunno what coderabbit is generating unit tests for tho

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

❌ Failed to create PR with unit tests: AGENT_CHAT: Failed to open pull request

@VikramAditya33
Copy link
Copy Markdown
Collaborator

Hey @VikramAditya33 have a look at the pr , all the checks have passed
I dunno what coderabbit is generating unit tests for tho

I'm wondering the same thing too I just triggered a review lol although everything LGTM. Probably just wait for a while, meanwhile let's just watch what coderabbit comes up with 😆

@aryan55254
Copy link
Copy Markdown
Contributor Author

@VikramAditya33 coderabbit doesn't seem to do anything lol , ig this can be merged ??

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/http/core/request.spec.ts (1)

445-461: Test passes incidentally — two lines are misleading no-ops.

Two issues that don't break the assertion today but obscure intent and are fragile:

  1. Line 449: mockUwsReq.getMethod.mockReturnValue('GET') runs after the UwsRequest constructors at lines 446–447. The constructor synchronously caches this.method from uwsReq.getMethod() (request.ts line 182), so this mock change has no effect on req1/req2. The test only passes because the default beforeEach mock already returns 'get' (uppercased to 'GET').

  2. Line 453: With no setHeaders([...]) call, neither content-length nor transfer-encoding is set, so _initBodyParser returns early at request.ts line 874–877 and never registers onData. The onDataCallback reference here still points to whatever the previous test installed (or the initial throwing stub if this test ran in isolation). The call therefore doesn't feed an empty body into req1/req2 — they already have doneReadingData = true, which is what actually drives json() down the empty-body branch.

Tightening the test makes the optimization being verified unambiguous and removes cross-test coupling on onDataCallback:

♻️ Suggested cleanup
       it('should optimize empty bodies by using the exact same memory reference', async () => {
+        // No content-length / transfer-encoding => _initBodyParser short-circuits,
+        // doneReadingData stays true, and json() takes the empty-body GET branch.
+        mockUwsReq.getMethod.mockReturnValue('get');
         const req1 = new UwsRequest(mockUwsReq, mockUwsRes);
         const req2 = new UwsRequest(mockUwsReq, mockUwsRes);
 
-        mockUwsReq.getMethod.mockReturnValue('GET');
         req1._initBodyParser(1024);
         req2._initBodyParser(1024);
 
-        onDataCallback(toArrayBuffer(Buffer.from('')), true);
-
         const res1 = await req1.json();
         const res2 = await req2.json();
 
         // Referential equality proves zero new objects were allocated
         expect(res1).toBe(res2);
         expect(Object.isFrozen(res1)).toBe(true);
       });

Optionally also assert expect(res1).toEqual({}) to pin the shape, and add a sibling test for HEAD/DELETE and one negative test that POST with empty body still throws — that would lock in the full contract documented at request.ts lines 1066–1073.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/http/core/request.spec.ts` around lines 445 - 461, The test relies on
incidental state and should explicitly configure method and headers so the body
parser is actually wired: construct UwsRequest instances after mocking
mockUwsReq.getMethod to the intended verb (e.g., 'GET' or 'HEAD'), call
setHeaders(...) to set either content-length: '0' or transfer-encoding to ensure
_initBodyParser registers onData (so onDataCallback is the one used by these
instances), then call _initBodyParser(1024) and invoke onDataCallback with an
empty buffer before calling json(); reference UwsRequest, _initBodyParser,
onDataCallback, json, setHeaders, and the header names
content-length/transfer-encoding when making these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/http/core/request.spec.ts`:
- Around line 445-461: The test relies on incidental state and should explicitly
configure method and headers so the body parser is actually wired: construct
UwsRequest instances after mocking mockUwsReq.getMethod to the intended verb
(e.g., 'GET' or 'HEAD'), call setHeaders(...) to set either content-length: '0'
or transfer-encoding to ensure _initBodyParser registers onData (so
onDataCallback is the one used by these instances), then call
_initBodyParser(1024) and invoke onDataCallback with an empty buffer before
calling json(); reference UwsRequest, _initBodyParser, onDataCallback, json,
setHeaders, and the header names content-length/transfer-encoding when making
these changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 386f3629-ece4-48c6-9875-84238774139a

📥 Commits

Reviewing files that changed from the base of the PR and between 7468f48 and 528c197.

📒 Files selected for processing (2)
  • src/http/core/request.spec.ts
  • src/http/core/request.ts

@VikramAditya33
Copy link
Copy Markdown
Collaborator

@aryan55254 does the nitpick regarding cleanup makes sense to you? Seems like a cleanup if you feel like it's okay otherwise I'm satisfied with the current implementation

@aryan55254
Copy link
Copy Markdown
Contributor Author

yeah I am satisified with it as well , i don't fully understand what the nitpick is saying tbh , it kinda seems to say the test is only passing because both req are empty but we were optimizing for when the requests are empty anyways so , and the memory reference matches so its solid from my side

Copy link
Copy Markdown
Collaborator

@VikramAditya33 VikramAditya33 left a comment

Choose a reason for hiding this comment

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

Alright it's a nitpick after all we can completely ignore it. LGTM, Thank you.

@VikramAditya33 VikramAditya33 merged commit 1ead869 into FOSSFORGE:main Apr 27, 2026
4 checks passed
@aryan55254
Copy link
Copy Markdown
Contributor Author

cool

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.

Consider using a static frozen empty object constant instead of hardcoding

2 participants