Skip to content

Commit 18dacfa

Browse files
authored
Merge pull request #2513 from murgatroid99/grpc-js_keepalive_time_fix
grpc-js: Fix keepalive ping timing after inactivity
2 parents 3e13d84 + 42a0274 commit 18dacfa

File tree

3 files changed

+55
-28
lines changed

3 files changed

+55
-28
lines changed

packages/grpc-js/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@grpc/grpc-js",
3-
"version": "1.8.18",
3+
"version": "1.8.19",
44
"description": "gRPC Library for Node - pure JS implementation",
55
"homepage": "https://grpc.io/",
66
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",

packages/grpc-js/src/server-call.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -954,8 +954,8 @@ export class Http2ServerCallStream<
954954
}
955955

956956
getPeer(): string {
957-
const socket = this.stream.session.socket;
958-
if (socket.remoteAddress) {
957+
const socket = this.stream.session?.socket;
958+
if (socket?.remoteAddress) {
959959
if (socket.remotePort) {
960960
return `${socket.remoteAddress}:${socket.remotePort}`;
961961
} else {

packages/grpc-js/src/transport.ts

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,12 @@ class Http2Transport implements Transport {
8181
/**
8282
* Timer reference for timeout that indicates when to send the next ping
8383
*/
84-
private keepaliveIntervalId: NodeJS.Timer;
84+
private keepaliveTimerId: NodeJS.Timer | null = null;
85+
/**
86+
* Indicates that the keepalive timer ran out while there were no active
87+
* calls, and a ping should be sent the next time a call starts.
88+
*/
89+
private pendingSendKeepalivePing = false;
8590
/**
8691
* Timer reference tracking when the most recent ping will be considered lost
8792
*/
@@ -142,10 +147,8 @@ class Http2Transport implements Transport {
142147
} else {
143148
this.keepaliveWithoutCalls = false;
144149
}
145-
this.keepaliveIntervalId = setTimeout(() => {}, 0);
146-
clearTimeout(this.keepaliveIntervalId);
147150
if (this.keepaliveWithoutCalls) {
148-
this.startKeepalivePings();
151+
this.maybeStartKeepalivePingTimer();
149152
}
150153

151154
this.subchannelAddressString = subchannelAddressToString(subchannelAddress);
@@ -295,6 +298,14 @@ class Http2Transport implements Transport {
295298
this.disconnectListeners.push(listener);
296299
}
297300

301+
private clearKeepaliveTimer() {
302+
if (!this.keepaliveTimerId) {
303+
return;
304+
}
305+
clearTimeout(this.keepaliveTimerId);
306+
this.keepaliveTimerId = null;
307+
}
308+
298309
private clearKeepaliveTimeout() {
299310
if (!this.keepaliveTimeoutId) {
300311
return;
@@ -303,7 +314,16 @@ class Http2Transport implements Transport {
303314
this.keepaliveTimeoutId = null;
304315
}
305316

306-
private sendPing() {
317+
private canSendPing() {
318+
return this.keepaliveTimeMs > 0 && (this.keepaliveWithoutCalls || this.activeCalls.size > 0);
319+
}
320+
321+
private maybeSendPing() {
322+
this.clearKeepaliveTimer();
323+
if (!this.canSendPing()) {
324+
this.pendingSendKeepalivePing = true;
325+
return;
326+
}
307327
if (this.channelzEnabled) {
308328
this.keepalivesSent += 1;
309329
}
@@ -320,6 +340,7 @@ class Http2Transport implements Transport {
320340
(err: Error | null, duration: number, payload: Buffer) => {
321341
this.keepaliveTrace('Received ping response');
322342
this.clearKeepaliveTimeout();
343+
this.maybeStartKeepalivePingTimer();
323344
}
324345
);
325346
} catch (e) {
@@ -329,46 +350,52 @@ class Http2Transport implements Transport {
329350
}
330351
}
331352

332-
private startKeepalivePings() {
333-
if (this.keepaliveTimeMs < 0) {
353+
/**
354+
* Starts the keepalive ping timer if appropriate. If the timer already ran
355+
* out while there were no active requests, instead send a ping immediately.
356+
* If the ping timer is already running or a ping is currently in flight,
357+
* instead do nothing and wait for them to resolve.
358+
*/
359+
private maybeStartKeepalivePingTimer() {
360+
if (!this.canSendPing()) {
334361
return;
335362
}
336-
this.keepaliveIntervalId = setInterval(() => {
337-
this.sendPing();
338-
}, this.keepaliveTimeMs);
339-
this.keepaliveIntervalId.unref?.();
340-
/* Don't send a ping immediately because whatever caused us to start
341-
* sending pings should also involve some network activity. */
363+
if (this.pendingSendKeepalivePing) {
364+
this.pendingSendKeepalivePing = false;
365+
this.maybeSendPing();
366+
} else if (!this.keepaliveTimerId && !this.keepaliveTimeoutId) {
367+
this.keepaliveTrace('Starting keepalive timer for ' + this.keepaliveTimeMs + 'ms');
368+
this.keepaliveTimerId = setTimeout(() => {
369+
this.maybeSendPing();
370+
}, this.keepaliveTimeMs).unref?.();
371+
}
372+
/* Otherwise, there is already either a keepalive timer or a ping pending,
373+
* wait for those to resolve. */
342374
}
343375

344-
/**
345-
* Stop keepalive pings when terminating a connection. This discards the
346-
* outstanding ping timeout, so it should not be called if the same
347-
* connection will still be used.
348-
*/
349376
private stopKeepalivePings() {
350-
clearInterval(this.keepaliveIntervalId);
377+
if (this.keepaliveTimerId) {
378+
clearTimeout(this.keepaliveTimerId);
379+
this.keepaliveTimerId = null;
380+
}
351381
this.clearKeepaliveTimeout();
352382
}
353383

354384
private removeActiveCall(call: Http2SubchannelCall) {
355385
this.activeCalls.delete(call);
356386
if (this.activeCalls.size === 0) {
357387
this.session.unref();
358-
if (!this.keepaliveWithoutCalls) {
359-
this.stopKeepalivePings();
360-
}
361388
}
362389
}
363390

364391
private addActiveCall(call: Http2SubchannelCall) {
365-
if (this.activeCalls.size === 0) {
392+
this.activeCalls.add(call);
393+
if (this.activeCalls.size === 1) {
366394
this.session.ref();
367395
if (!this.keepaliveWithoutCalls) {
368-
this.startKeepalivePings();
396+
this.maybeStartKeepalivePingTimer();
369397
}
370398
}
371-
this.activeCalls.add(call);
372399
}
373400

374401
createCall(metadata: Metadata, host: string, method: string, listener: SubchannelCallInterceptingListener, subchannelCallStatsTracker: Partial<CallEventTracker>): Http2SubchannelCall {

0 commit comments

Comments
 (0)