Skip to content

Commit e866a45

Browse files
authored
perf: Keep weak reference to session for re-use (nodejs#1802)
1 parent 78060b6 commit e866a45

File tree

1 file changed

+62
-19
lines changed

1 file changed

+62
-19
lines changed

lib/core/connect.js

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,81 @@ const net = require('net')
44
const assert = require('assert')
55
const util = require('./util')
66
const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
7+
78
let tls // include tls conditionally since it is not always available
89

910
// TODO: session re-use does not wait for the first
1011
// connection to resolve the session and might therefore
1112
// resolve the same servername multiple times even when
1213
// re-use is enabled.
1314

15+
let SessionCache
16+
if (global.FinalizationRegistry) {
17+
SessionCache = class WeakSessionCache {
18+
constructor (maxCachedSessions) {
19+
this._maxCachedSessions = maxCachedSessions
20+
this._sessionCache = new Map()
21+
this._sessionRegistry = new global.FinalizationRegistry((key) => {
22+
if (this._sessionCache.size < this._maxCachedSessions) {
23+
return
24+
}
25+
26+
const ref = this._sessionCache.get(key)
27+
if (ref !== undefined && ref.deref() === undefined) {
28+
this._sessionCache.delete(key)
29+
}
30+
})
31+
}
32+
33+
get (sessionKey) {
34+
const ref = this._sessionCache.get(sessionKey)
35+
return ref ? ref.deref() : null
36+
}
37+
38+
set (sessionKey, session) {
39+
if (this._maxCachedSessions === 0) {
40+
return
41+
}
42+
43+
this._sessionCache.set(sessionKey, new WeakRef(session))
44+
this._sessionRegistry.register(session, sessionKey)
45+
}
46+
}
47+
} else {
48+
SessionCache = class SimpleSessionCache {
49+
constructor (maxCachedSessions) {
50+
this._maxCachedSessions = maxCachedSessions
51+
this._sessionCache = new Map()
52+
}
53+
54+
get (sessionKey) {
55+
return this._sessionCache.get(sessionKey)
56+
}
57+
58+
set (sessionKey, session) {
59+
if (this._maxCachedSessions === 0) {
60+
return
61+
}
62+
63+
if (this._sessionCache.size >= this._maxCachedSessions) {
64+
// remove the oldest session
65+
const { value: oldestKey } = this._sessionCache.keys().next()
66+
this._sessionCache.delete(oldestKey)
67+
}
68+
69+
this._sessionCache.set(sessionKey, session)
70+
}
71+
}
72+
}
73+
1474
function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
1575
if (maxCachedSessions != null && (!Number.isInteger(maxCachedSessions) || maxCachedSessions < 0)) {
1676
throw new InvalidArgumentError('maxCachedSessions must be a positive integer or zero')
1777
}
1878

1979
const options = { path: socketPath, ...opts }
20-
const sessionCache = new Map()
80+
const sessionCache = new SessionCache(maxCachedSessions == null ? 100 : maxCachedSessions)
2181
timeout = timeout == null ? 10e3 : timeout
22-
maxCachedSessions = maxCachedSessions == null ? 100 : maxCachedSessions
2382

2483
return function connect ({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) {
2584
let socket
@@ -47,25 +106,9 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
47106

48107
socket
49108
.on('session', function (session) {
50-
// cache is disabled
51-
if (maxCachedSessions === 0) {
52-
return
53-
}
54-
55-
if (sessionCache.size >= maxCachedSessions) {
56-
// remove the oldest session
57-
const { value: oldestKey } = sessionCache.keys().next()
58-
sessionCache.delete(oldestKey)
59-
}
60-
109+
// TODO (fix): Can a session become invalid once established? Don't think so?
61110
sessionCache.set(sessionKey, session)
62111
})
63-
.on('error', function (err) {
64-
if (sessionKey && err.code !== 'UND_ERR_INFO') {
65-
// TODO (fix): Only delete for session related errors.
66-
sessionCache.delete(sessionKey)
67-
}
68-
})
69112
} else {
70113
assert(!httpSocket, 'httpSocket can only be sent on TLS update')
71114
socket = net.connect({

0 commit comments

Comments
 (0)