subchannel-pool.js
5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
"use strict";
/*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSubchannelPool = exports.SubchannelPool = void 0;
const channel_options_1 = require("./channel-options");
const subchannel_1 = require("./subchannel");
const uri_parser_1 = require("./uri-parser");
// 10 seconds in milliseconds. This value is arbitrary.
/**
* The amount of time in between checks for dropping subchannels that have no
* other references
*/
const REF_CHECK_INTERVAL = 10000;
class SubchannelPool {
/**
* A pool of subchannels use for making connections. Subchannels with the
* exact same parameters will be reused.
* @param global If true, this is the global subchannel pool. Otherwise, it
* is the pool for a single channel.
*/
constructor(global) {
this.global = global;
this.pool = Object.create(null);
/**
* A timer of a task performing a periodic subchannel cleanup.
*/
this.cleanupTimer = null;
}
/**
* Unrefs all unused subchannels and cancels the cleanup task if all
* subchannels have been unrefed.
*/
unrefUnusedSubchannels() {
let allSubchannelsUnrefed = true;
/* These objects are created with Object.create(null), so they do not
* have a prototype, which means that for (... in ...) loops over them
* do not need to be filtered */
// eslint-disable-disable-next-line:forin
for (const channelTarget in this.pool) {
const subchannelObjArray = this.pool[channelTarget];
const refedSubchannels = subchannelObjArray.filter((value) => !value.subchannel.unrefIfOneRef());
if (refedSubchannels.length > 0) {
allSubchannelsUnrefed = false;
}
/* For each subchannel in the pool, try to unref it if it has
* exactly one ref (which is the ref from the pool itself). If that
* does happen, remove the subchannel from the pool */
this.pool[channelTarget] = refedSubchannels;
}
/* Currently we do not delete keys with empty values. If that results
* in significant memory usage we should change it. */
// Cancel the cleanup task if all subchannels have been unrefed.
if (allSubchannelsUnrefed && this.cleanupTimer !== null) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = null;
}
}
/**
* Ensures that the cleanup task is spawned.
*/
ensureCleanupTask() {
var _a, _b;
if (this.global && this.cleanupTimer === null) {
this.cleanupTimer = setInterval(() => {
this.unrefUnusedSubchannels();
}, REF_CHECK_INTERVAL);
// Unref because this timer should not keep the event loop running.
// Call unref only if it exists to address electron/electron#21162
(_b = (_a = this.cleanupTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
}
}
/**
* Get a subchannel if one already exists with exactly matching parameters.
* Otherwise, create and save a subchannel with those parameters.
* @param channelTarget
* @param subchannelTarget
* @param channelArguments
* @param channelCredentials
*/
getOrCreateSubchannel(channelTargetUri, subchannelTarget, channelArguments, channelCredentials) {
this.ensureCleanupTask();
const channelTarget = uri_parser_1.uriToString(channelTargetUri);
if (channelTarget in this.pool) {
const subchannelObjArray = this.pool[channelTarget];
for (const subchannelObj of subchannelObjArray) {
if (subchannel_1.subchannelAddressEqual(subchannelTarget, subchannelObj.subchannelAddress) &&
channel_options_1.channelOptionsEqual(channelArguments, subchannelObj.channelArguments) &&
channelCredentials._equals(subchannelObj.channelCredentials)) {
return subchannelObj.subchannel;
}
}
}
// If we get here, no matching subchannel was found
const subchannel = new subchannel_1.Subchannel(channelTargetUri, subchannelTarget, channelArguments, channelCredentials);
if (!(channelTarget in this.pool)) {
this.pool[channelTarget] = [];
}
this.pool[channelTarget].push({
subchannelAddress: subchannelTarget,
channelArguments,
channelCredentials,
subchannel,
});
if (this.global) {
subchannel.ref();
}
return subchannel;
}
}
exports.SubchannelPool = SubchannelPool;
const globalSubchannelPool = new SubchannelPool(true);
/**
* Get either the global subchannel pool, or a new subchannel pool.
* @param global
*/
function getSubchannelPool(global) {
if (global) {
return globalSubchannelPool;
}
else {
return new SubchannelPool(false);
}
}
exports.getSubchannelPool = getSubchannelPool;
//# sourceMappingURL=subchannel-pool.js.map