Skip to content

Commit da6d779

Browse files
Prototype optimisations (#211)
* Add memory logging and reduce storage lengths * Update doc
1 parent e19626c commit da6d779

File tree

11 files changed

+2237
-31
lines changed

11 files changed

+2237
-31
lines changed

app.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Suppress deprecation warning from dependencies using util.isArray
2+
process.noDeprecation = true
3+
14
// Core dependencies
25
const {
36
createReadStream,
@@ -96,7 +99,7 @@ const sessionName = `nhsuk-prototype-kit-${Buffer.from(config.serviceName, 'utf8
9699
const sessionOptions = {
97100
secret: sessionName,
98101
cookie: {
99-
maxAge: 1000 * 60 * 60 * 4 // 4 hours
102+
maxAge: 1000 * 60 * 60 * 1 // 1 hour (reduced from 4 to limit session accumulation)
100103
}
101104
}
102105

app/lib/utils/regenerate-data.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,19 @@ async function regenerateData(req) {
1111
const generatedDataPath = resolve(dataDirectory, 'generated')
1212
const generationInfoPath = join(generatedDataPath, 'generation-info.json')
1313

14+
// Also clear lib/utils.js cache so its sessionDataDefaults gets refreshed
15+
// (it caches sessionDataDefaults at module load time)
16+
const libUtilsPath = resolve(__dirname, '../../../lib/utils.js')
17+
1418
// Generate new data
1519
await generateData()
1620

1721
// Clear the require cache for session data defaults
1822
delete require.cache[require.resolve(sessionDataPath)]
1923

24+
// Clear lib/utils.js cache so it reloads with fresh sessionDataDefaults
25+
delete require.cache[require.resolve(libUtilsPath)]
26+
2027
// Clear cache for the generated JSON files
2128
Object.keys(require.cache).forEach((key) => {
2229
if (key.startsWith(generatedDataPath)) {

app/routes.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,54 @@ const { resetCallSequence } = require('./lib/utils/random')
1010

1111
const router = express.Router()
1212

13+
// Memory logging - tracks growth and logs significant changes
14+
let requestCount = 0
15+
let lastLoggedRss = 0
16+
const logMemory = (context, sessionData) => {
17+
const mem = process.memoryUsage()
18+
const rss = mem.rss / 1024 / 1024
19+
let sessionInfo = ''
20+
if (sessionData) {
21+
try {
22+
const sessionStr = JSON.stringify(sessionData)
23+
sessionInfo = `, session: ${(sessionStr.length / 1024 / 1024).toFixed(2)}MB`
24+
} catch (e) {
25+
sessionInfo = ', session: [error]'
26+
}
27+
}
28+
console.log(
29+
`[Memory ${context}] heap: ${(mem.heapUsed / 1024 / 1024).toFixed(1)}MB, rss: ${rss.toFixed(1)}MB, requests: ${requestCount}${sessionInfo}`
30+
)
31+
lastLoggedRss = rss
32+
}
33+
34+
// Log memory on startup
35+
logMemory('startup')
36+
1337
// Keep regeneration middleware before other routes
1438
router.use(async (req, res, next) => {
1539
try {
40+
requestCount++
41+
const isStaticRequest = req.path.match(
42+
/\.(js|css|ico|png|jpg|svg|woff|woff2)$/
43+
)
44+
45+
// Log memory if RSS has grown significantly (>20MB) since last log
46+
const currentRss = process.memoryUsage().rss / 1024 / 1024
47+
if (!isStaticRequest && currentRss - lastLoggedRss > 20) {
48+
logMemory(`growth on ${req.method} ${req.path}`, req.session?.data)
49+
}
50+
51+
// Also log periodically
52+
if (!isStaticRequest && requestCount % 50 === 0) {
53+
logMemory(`request #${requestCount}`, req.session?.data)
54+
}
55+
1656
if (needsRegeneration(req.session.data?.generationInfo)) {
57+
logMemory('before-regeneration')
1758
console.log('Regenerating data for new day...')
1859
await regenerateData(req)
60+
logMemory('after-regeneration')
1961
}
2062
next()
2163
} catch (err) {

app/routes/events.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2819,4 +2819,4 @@ module.exports = (router) => {
28192819
)
28202820
res.redirect(returnUrl)
28212821
})
2822-
}
2822+
}

lib/middleware/prototype-admin-routes.js

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,35 @@ router.all('/reset-session-data', (req, res) => {
5959
returnPage = '/'
6060
}
6161

62-
console.log('Clearing session data')
62+
// Log memory before reset
63+
const memBefore = process.memoryUsage()
64+
console.log(
65+
`[Memory before-reset] heap: ${(memBefore.heapUsed / 1024 / 1024).toFixed(1)}MB, rss: ${(memBefore.rss / 1024 / 1024).toFixed(1)}MB`
66+
)
6367

6468
const dataDirectory = join(__dirname, '../../app/data')
6569
const sessionDataPath = resolve(dataDirectory, 'session-data-defaults.js')
66-
const generatedDataPath = resolve(dataDirectory, 'generated')
67-
68-
// Clear the require cache for session data defaults
69-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
70-
delete require.cache[require.resolve(sessionDataPath)]
71-
72-
// Clear cache for the generated JSON files
73-
Object.keys(require.cache).forEach((key) => {
74-
if (key.startsWith(generatedDataPath)) {
75-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
76-
delete require.cache[key]
77-
}
78-
})
7970

80-
// Load fresh session defaults after clearing cache
81-
req.session.data = require(sessionDataPath)
71+
// Get the cached session defaults
72+
// Note: We don't clear require cache here as that causes memory growth
73+
// when done repeatedly (JSON parsing artifacts don't get GC'd properly).
74+
// The autoStoreData middleware will merge these defaults with session data
75+
// on the next request anyway.
76+
const sessionDefaults = require(sessionDataPath)
77+
78+
// Clear existing session data and reset to defaults
79+
// Using Object.assign to create a shallow copy - the arrays (participants,
80+
// events, clinics) remain shared references which is fine since sessions
81+
// don't typically modify the original arrays, just individual items
82+
Object.keys(req.session.data).forEach((key) => delete req.session.data[key])
83+
Object.assign(req.session.data, sessionDefaults)
84+
85+
// Log memory after reset
86+
const memAfter = process.memoryUsage()
87+
console.log(
88+
`[Memory after-reset] heap: ${(memAfter.heapUsed / 1024 / 1024).toFixed(1)}MB, rss: ${(memAfter.rss / 1024 / 1024).toFixed(1)}MB`
89+
)
90+
8291
req.flash('success', 'Session data cleared')
8392

8493
res.render('reset-done', {

0 commit comments

Comments
 (0)