Skip to content

Commit 2140577

Browse files
feat: add configuration for minimum number of channels (GoogleCloudPlatform#136)
* feat: add configuration for minimum number of channels * add a test and regenerate protos
1 parent a05c3dd commit 2140577

File tree

5 files changed

+76
-12
lines changed

5 files changed

+76
-12
lines changed

protos/grpc_gcp.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ message ApiConfig {
2727
message ChannelPoolConfig {
2828
// The max number of channels in the pool.
2929
uint32 max_size = 1;
30+
// The min number of channels in the pool.
31+
uint32 min_size = 4;
3032
// The idle timeout (seconds) of channels without bound affinity sessions.
3133
uint64 idle_timeout = 2;
3234
// The low watermark of max number of concurrent streams in a channel.

src/gcp_channel_factory.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export function getGcpChannelFactoryClass(
5252
* A channel management factory that implements grpc.Channel APIs.
5353
*/
5454
return class GcpChannelFactory implements GcpChannelFactoryInterface {
55+
private minSize: number;
5556
private maxSize: number;
5657
private maxConcurrentStreamsLowWatermark: number;
5758
private options: {};
@@ -80,17 +81,23 @@ export function getGcpChannelFactoryClass(
8081
'Channel options must be an object with string keys and integer or string values'
8182
);
8283
}
84+
this.minSize = 1;
8385
this.maxSize = 10;
8486
this.maxConcurrentStreamsLowWatermark = 100;
8587
const gcpApiConfig = options.gcpApiConfig;
8688
if (gcpApiConfig) {
8789
if (gcpApiConfig.channelPool) {
8890
const channelPool = gcpApiConfig.channelPool;
91+
if (channelPool.minSize) this.minSize = channelPool.minSize;
8992
if (channelPool.maxSize) this.maxSize = channelPool.maxSize;
9093
if (channelPool.maxConcurrentStreamsLowWatermark) {
9194
this.maxConcurrentStreamsLowWatermark =
9295
channelPool.maxConcurrentStreamsLowWatermark;
9396
}
97+
98+
if (this.maxSize < this.minSize) {
99+
throw new Error('Invalid channelPool config: minSize must <= maxSize')
100+
}
94101
}
95102
this.initMethodToAffinityMap(gcpApiConfig);
96103
}
@@ -99,8 +106,11 @@ export function getGcpChannelFactoryClass(
99106
this.options = options;
100107
this.target = address;
101108
this.credentials = credentials;
102-
// Initialize channel in the pool to avoid empty pool.
103-
this.getChannelRef();
109+
110+
// Create initial channels
111+
for (let i = 0; i < this.minSize; i++) {
112+
this.addChannel();
113+
}
104114
}
105115

106116
getChannelzRef() {
@@ -154,21 +164,30 @@ export function getGcpChannelFactoryClass(
154164
// If all existing channels are busy, and channel pool still has capacity,
155165
// create a new channel in the pool.
156166
if (size < this.maxSize) {
157-
const channelOptions = Object.assign(
167+
return this.addChannel();
168+
} else {
169+
return this.channelRefs[0];
170+
}
171+
}
172+
173+
/**
174+
* Create a new channel and add it to the pool.
175+
* @private
176+
*/
177+
private addChannel() : ChannelRef {
178+
const size = this.channelRefs.length;
179+
const channelOptions = Object.assign(
158180
{[CLIENT_CHANNEL_ID]: size},
159181
this.options
160-
);
161-
const grpcChannel = new grpc.Channel(
182+
);
183+
const grpcChannel = new grpc.Channel(
162184
this.target,
163185
this.credentials,
164186
channelOptions
165-
);
166-
const channelRef = new ChannelRef(grpcChannel, size);
167-
this.channelRefs.push(channelRef);
168-
return channelRef;
169-
} else {
170-
return this.channelRefs[0];
171-
}
187+
);
188+
const channelRef = new ChannelRef(grpcChannel, size);
189+
this.channelRefs.push(channelRef);
190+
return channelRef;
172191
}
173192

174193
/**

src/generated/grpc_gcp.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ export namespace grpc {
107107
/** ChannelPoolConfig maxSize */
108108
maxSize?: (number|null);
109109

110+
/** ChannelPoolConfig minSize */
111+
minSize?: (number|null);
112+
110113
/** ChannelPoolConfig idleTimeout */
111114
idleTimeout?: (number|Long|null);
112115

@@ -126,6 +129,9 @@ export namespace grpc {
126129
/** ChannelPoolConfig maxSize. */
127130
public maxSize: number;
128131

132+
/** ChannelPoolConfig minSize. */
133+
public minSize: number;
134+
129135
/** ChannelPoolConfig idleTimeout. */
130136
public idleTimeout: (number|Long);
131137

src/generated/grpc_gcp.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ $root.grpc = (function() {
270270
* @memberof grpc.gcp
271271
* @interface IChannelPoolConfig
272272
* @property {number|null} [maxSize] ChannelPoolConfig maxSize
273+
* @property {number|null} [minSize] ChannelPoolConfig minSize
273274
* @property {number|Long|null} [idleTimeout] ChannelPoolConfig idleTimeout
274275
* @property {number|null} [maxConcurrentStreamsLowWatermark] ChannelPoolConfig maxConcurrentStreamsLowWatermark
275276
*/
@@ -297,6 +298,14 @@ $root.grpc = (function() {
297298
*/
298299
ChannelPoolConfig.prototype.maxSize = 0;
299300

301+
/**
302+
* ChannelPoolConfig minSize.
303+
* @member {number} minSize
304+
* @memberof grpc.gcp.ChannelPoolConfig
305+
* @instance
306+
*/
307+
ChannelPoolConfig.prototype.minSize = 0;
308+
300309
/**
301310
* ChannelPoolConfig idleTimeout.
302311
* @member {number|Long} idleTimeout
@@ -343,6 +352,8 @@ $root.grpc = (function() {
343352
writer.uint32(/* id 2, wireType 0 =*/16).uint64(message.idleTimeout);
344353
if (message.maxConcurrentStreamsLowWatermark != null && Object.hasOwnProperty.call(message, "maxConcurrentStreamsLowWatermark"))
345354
writer.uint32(/* id 3, wireType 0 =*/24).uint32(message.maxConcurrentStreamsLowWatermark);
355+
if (message.minSize != null && Object.hasOwnProperty.call(message, "minSize"))
356+
writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.minSize);
346357
return writer;
347358
};
348359

@@ -380,6 +391,9 @@ $root.grpc = (function() {
380391
case 1:
381392
message.maxSize = reader.uint32();
382393
break;
394+
case 4:
395+
message.minSize = reader.uint32();
396+
break;
383397
case 2:
384398
message.idleTimeout = reader.uint64();
385399
break;
@@ -424,6 +438,9 @@ $root.grpc = (function() {
424438
if (message.maxSize != null && message.hasOwnProperty("maxSize"))
425439
if (!$util.isInteger(message.maxSize))
426440
return "maxSize: integer expected";
441+
if (message.minSize != null && message.hasOwnProperty("minSize"))
442+
if (!$util.isInteger(message.minSize))
443+
return "minSize: integer expected";
427444
if (message.idleTimeout != null && message.hasOwnProperty("idleTimeout"))
428445
if (!$util.isInteger(message.idleTimeout) && !(message.idleTimeout && $util.isInteger(message.idleTimeout.low) && $util.isInteger(message.idleTimeout.high)))
429446
return "idleTimeout: integer|Long expected";
@@ -447,6 +464,8 @@ $root.grpc = (function() {
447464
var message = new $root.grpc.gcp.ChannelPoolConfig();
448465
if (object.maxSize != null)
449466
message.maxSize = object.maxSize >>> 0;
467+
if (object.minSize != null)
468+
message.minSize = object.minSize >>> 0;
450469
if (object.idleTimeout != null)
451470
if ($util.Long)
452471
(message.idleTimeout = $util.Long.fromValue(object.idleTimeout)).unsigned = true;
@@ -482,6 +501,7 @@ $root.grpc = (function() {
482501
} else
483502
object.idleTimeout = options.longs === String ? "0" : 0;
484503
object.maxConcurrentStreamsLowWatermark = 0;
504+
object.minSize = 0;
485505
}
486506
if (message.maxSize != null && message.hasOwnProperty("maxSize"))
487507
object.maxSize = message.maxSize;
@@ -492,6 +512,8 @@ $root.grpc = (function() {
492512
object.idleTimeout = options.longs === String ? $util.Long.prototype.toString.call(message.idleTimeout) : options.longs === Number ? new $util.LongBits(message.idleTimeout.low >>> 0, message.idleTimeout.high >>> 0).toNumber(true) : message.idleTimeout;
493513
if (message.maxConcurrentStreamsLowWatermark != null && message.hasOwnProperty("maxConcurrentStreamsLowWatermark"))
494514
object.maxConcurrentStreamsLowWatermark = message.maxConcurrentStreamsLowWatermark;
515+
if (message.minSize != null && message.hasOwnProperty("minSize"))
516+
object.minSize = message.minSize;
495517
return object;
496518
};
497519

test/unit/channel_factory_test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,21 @@ for (const grpcLibName of ['grpc', '@grpc/grpc-js']) {
100100
assert(channel instanceof grpcGcp.GcpChannelFactory);
101101
});
102102
});
103+
104+
it('should build min channels', () => {
105+
const channel = new grpcGcp.GcpChannelFactory(
106+
'hostname',
107+
insecureCreds,
108+
{
109+
gcpApiConfig: grpcGcp.createGcpApiConfig({
110+
"channelPool": {
111+
"minSize": 3
112+
}
113+
})
114+
}
115+
);
116+
assert.equal(channel.channelRefs.length, 3);
117+
})
103118
});
104119
describe('close', () => {
105120
let channel;

0 commit comments

Comments
 (0)