Skip to content

Commit 0a5a232

Browse files
committed
grpc-js: Fix pick first shutdown reference handling
1 parent 8f3d4fb commit 0a5a232

File tree

2 files changed

+80
-3
lines changed

2 files changed

+80
-3
lines changed

packages/grpc-js/src/load-balancer-pick-first.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -449,11 +449,15 @@ export class PickFirstLoadBalancer implements LoadBalancer {
449449
destroy() {
450450
this.resetSubchannelList();
451451
if (this.currentPick !== null) {
452-
this.currentPick.unref();
453-
this.currentPick.removeConnectivityStateListener(
452+
/* Unref can cause a state change, which can cause a change in the value
453+
* of this.currentPick, so we hold a local reference to make sure that
454+
* does not impact this function. */
455+
const currentPick = this.currentPick;
456+
currentPick.unref();
457+
currentPick.removeConnectivityStateListener(
454458
this.pickedSubchannelStateListener
455459
);
456-
this.channelControlHelper.removeChannelzChild(this.currentPick.getChannelzRef());
460+
this.channelControlHelper.removeChannelzChild(currentPick.getChannelzRef());
457461
}
458462
}
459463

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2022 gRPC authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
import * as assert from 'assert';
19+
import * as path from 'path';
20+
import * as grpc from '../src';
21+
import { sendUnaryData, Server, ServerCredentials, ServerUnaryCall, ServiceClientConstructor, ServiceError } from "../src";
22+
import { loadProtoFile } from './common';
23+
24+
const protoFile = path.join(__dirname, 'fixtures', 'echo_service.proto');
25+
const echoService = loadProtoFile(protoFile)
26+
.EchoService as ServiceClientConstructor;
27+
28+
describe('Local subchannel pool', () => {
29+
let server: Server;
30+
let serverPort: number;
31+
32+
before(done => {
33+
34+
server = new Server();
35+
server.addService(echoService.service, {
36+
echo(call: ServerUnaryCall<any, any>, callback: sendUnaryData<any>) {
37+
callback(null, call.request);
38+
},
39+
});
40+
41+
server.bindAsync(
42+
'localhost:0',
43+
ServerCredentials.createInsecure(),
44+
(err, port) => {
45+
assert.ifError(err);
46+
serverPort = port;
47+
server.start();
48+
done();
49+
}
50+
);
51+
});
52+
53+
after(done => {
54+
server.tryShutdown(done);
55+
});
56+
57+
it('should complete the client lifecycle without error', done => {
58+
const client = new echoService(
59+
`localhost:${serverPort}`,
60+
grpc.credentials.createInsecure(),
61+
{'grpc.use_local_subchannel_pool': 1}
62+
);
63+
client.echo(
64+
{ value: 'test value', value2: 3 },
65+
(error: ServiceError, response: any) => {
66+
assert.ifError(error);
67+
assert.deepStrictEqual(response, { value: 'test value', value2: 3 });
68+
client.close();
69+
done();
70+
}
71+
);
72+
});
73+
});

0 commit comments

Comments
 (0)