Skip to content

Commit 8e6c13f

Browse files
committed
include garbage collection change
1 parent cdf31ba commit 8e6c13f

File tree

3 files changed

+85
-44
lines changed

3 files changed

+85
-44
lines changed

packages/pwa-kit-runtime/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## v2.9.1 (Mar 18, 2025)
22
- Disable CloudWatch metrics sender retries [#2321](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2321)
3+
- Remove forced garbage collection on each invocation. Set `FORCE_GC=true` for the old behavior. [#2321](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2321)
34

45
## v2.9.0 (Jan 21, 2025)
56
- Support Node 20 and NPM 10 in PWA Kit v2 [#2189](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2189)

packages/pwa-kit-runtime/src/ssr/server/build-remote-server.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ export const RemoteServerFactory = {
304304
const mixin = {
305305
options,
306306

307+
// Forcing a GC is no longer necessary, and will be
308+
// skipped by default (unless FORCE_GC env-var is set).
307309
_collectGarbage() {
308310
// Do global.gc in a separate 'then' handler so
309311
// that all major variables are out of scope and
@@ -823,12 +825,15 @@ export const RemoteServerFactory = {
823825
context.callbackWaitsForEmptyEventLoop = false
824826

825827
if (lambdaContainerReused) {
826-
// DESKTOP-434 If this Lambda container is being reused,
827-
// clean up memory now, so that we start with low usage.
828-
// These regular GC calls take about 80-100 mS each, as opposed
829-
// to forced GC calls, which occur randomly and can take several
830-
// hundred mS.
831-
app._collectGarbage()
828+
const forceGarbageCollection = process.env.FORCE_GC
829+
if (forceGarbageCollection && forceGarbageCollection.toLowerCase() === 'true') {
830+
// DESKTOP-434 If this Lambda container is being reused,
831+
// clean up memory now, so that we start with low usage.
832+
// These regular GC calls take about 80-100 mS each, as opposed
833+
// to forced GC calls, which occur randomly and can take several
834+
// hundred mS.
835+
app._collectGarbage()
836+
}
832837
app.sendMetric('LambdaReused')
833838
} else {
834839
// This is the first use of this container, so set the

packages/pwa-kit-runtime/src/ssr/server/express.lambda.test.js

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,48 @@ export const httpsAgent = new https.Agent({
5252
rejectUnauthorized: false
5353
})
5454

55+
function createServerWithGCSpy() {
56+
const route = jest.fn((req, res) => {
57+
res.send('<html/>')
58+
})
59+
60+
const options = {
61+
buildDir: testFixtures,
62+
mobify: testPackageMobify,
63+
sslFilePath: path.join(testFixtures, 'localhost.pem'),
64+
quiet: true,
65+
port: TEST_PORT,
66+
fetchAgents: {
67+
https: httpsAgent
68+
}
69+
}
70+
71+
const {handler, server, app} = RemoteServerFactory.createHandler(options, (app) => {
72+
app.get('/*', route)
73+
})
74+
75+
const collectGarbage = jest.spyOn(app, '_collectGarbage')
76+
const sendMetric = jest.spyOn(app, 'sendMetric')
77+
return {route, handler, collectGarbage, sendMetric, server}
78+
}
79+
80+
function createApiGatewayEvent() {
81+
// Set up a fake event and a fake context for the Lambda call
82+
const event = createEvent('aws:apiGateway', {
83+
path: '/',
84+
body: undefined
85+
})
86+
87+
if (event.queryStringParameters) {
88+
delete event.queryStringParameters
89+
}
90+
91+
const context = AWSMockContext({
92+
functionName: 'SSRTest'
93+
})
94+
return {event, context}
95+
}
96+
5597
describe('SSRServer Lambda integration', () => {
5698
let savedEnvironment
5799
let server
@@ -371,47 +413,40 @@ describe('SSRServer Lambda integration', () => {
371413
})
372414
})
373415

374-
test('Lambda reuse behaviour', () => {
375-
const route = jest.fn((req, res) => {
376-
res.send('<html/>')
377-
})
378-
379-
const options = {
380-
buildDir: testFixtures,
381-
mobify: testPackageMobify,
382-
sslFilePath: path.join(testFixtures, 'localhost.pem'),
383-
quiet: true,
384-
port: TEST_PORT,
385-
fetchAgents: {
386-
https: httpsAgent
387-
}
388-
}
389-
390-
const {
391-
app,
392-
handler,
393-
server: srv
394-
} = RemoteServerFactory.createHandler(options, (app) => {
395-
app.get('/*', route)
396-
})
416+
test('Lambda reuse -- Default Behavior', () => {
417+
const {route, handler, collectGarbage, sendMetric, new_server} = createServerWithGCSpy()
418+
const {event, context} = createApiGatewayEvent()
419+
server = new_server
397420

398-
const collectGarbage = jest.spyOn(app, '_collectGarbage')
399-
const sendMetric = jest.spyOn(app, 'sendMetric')
400-
server = srv
401-
402-
// Set up a fake event and a fake context for the Lambda call
403-
const event = createEvent('aws:apiGateway', {
404-
path: '/',
405-
body: undefined
406-
})
421+
const call = (event) =>
422+
new Promise((resolve) => handler(event, context, (err, response) => resolve(response)))
407423

408-
if (event.queryStringParameters) {
409-
delete event.queryStringParameters
410-
}
424+
return Promise.resolve()
425+
.then(() => call(event))
426+
.then((response) => {
427+
// First request - Lambda container created
428+
expect(response.statusCode).toBe(200)
429+
expect(collectGarbage.mock.calls).toHaveLength(0)
430+
expect(route.mock.calls).toHaveLength(1)
431+
expect(sendMetric).toHaveBeenCalledWith('LambdaCreated')
432+
expect(sendMetric).not.toHaveBeenCalledWith('LambdaReused')
433+
})
434+
.then(() => call(event))
435+
.then((response) => {
436+
// Second call - Lambda container reused
437+
expect(response.statusCode).toBe(200)
438+
expect(collectGarbage.mock.calls).toHaveLength(0)
439+
expect(route.mock.calls).toHaveLength(2)
440+
expect(sendMetric).toHaveBeenCalledWith('LambdaCreated')
441+
expect(sendMetric).toHaveBeenCalledWith('LambdaReused')
442+
})
443+
})
411444

412-
const context = AWSMockContext({
413-
functionName: 'SSRTest'
414-
})
445+
test('Lambda reuse -- with Forced Garbage Collection Enabled', () => {
446+
process.env.FORCE_GC = 'true'
447+
const {event, context} = createApiGatewayEvent()
448+
const {route, handler, collectGarbage, sendMetric, new_server} = createServerWithGCSpy()
449+
server = new_server
415450

416451
const call = (event) =>
417452
new Promise((resolve) => handler(event, context, (err, response) => resolve(response)))

0 commit comments

Comments
 (0)