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