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 | */
|
35 | constructor() {
|
36 | this.pool = Object.create(null);
|
37 | /**
|
38 | * A timer of a task performing a periodic subchannel cleanup.
|
39 | */
|
40 | this.cleanupTimer = null;
|
41 | }
|
42 | /**
|
43 | * Unrefs all unused subchannels and cancels the cleanup task if all
|
44 | * subchannels have been unrefed.
|
45 | */
|
46 | unrefUnusedSubchannels() {
|
47 | let allSubchannelsUnrefed = true;
|
48 | /* These objects are created with Object.create(null), so they do not
|
49 | * have a prototype, which means that for (... in ...) loops over them
|
50 | * do not need to be filtered */
|
51 | // eslint-disable-disable-next-line:forin
|
52 | for (const channelTarget in this.pool) {
|
53 | const subchannelObjArray = this.pool[channelTarget];
|
54 | const refedSubchannels = subchannelObjArray.filter((value) => !value.subchannel.unrefIfOneRef());
|
55 | if (refedSubchannels.length > 0) {
|
56 | allSubchannelsUnrefed = false;
|
57 | }
|
58 | /* For each subchannel in the pool, try to unref it if it has
|
59 | * exactly one ref (which is the ref from the pool itself). If that
|
60 | * does happen, remove the subchannel from the pool */
|
61 | this.pool[channelTarget] = refedSubchannels;
|
62 | }
|
63 | /* Currently we do not delete keys with empty values. If that results
|
64 | * in significant memory usage we should change it. */
|
65 | // Cancel the cleanup task if all subchannels have been unrefed.
|
66 | if (allSubchannelsUnrefed && this.cleanupTimer !== null) {
|
67 | clearInterval(this.cleanupTimer);
|
68 | this.cleanupTimer = null;
|
69 | }
|
70 | }
|
71 | /**
|
72 | * Ensures that the cleanup task is spawned.
|
73 | */
|
74 | ensureCleanupTask() {
|
75 | var _a, _b;
|
76 | if (this.cleanupTimer === null) {
|
77 | this.cleanupTimer = setInterval(() => {
|
78 | this.unrefUnusedSubchannels();
|
79 | }, REF_CHECK_INTERVAL);
|
80 | // Unref because this timer should not keep the event loop running.
|
81 | // Call unref only if it exists to address electron/electron#21162
|
82 | (_b = (_a = this.cleanupTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
|
83 | }
|
84 | }
|
85 | /**
|
86 | * Get a subchannel if one already exists with exactly matching parameters.
|
87 | * Otherwise, create and save a subchannel with those parameters.
|
88 | * @param channelTarget
|
89 | * @param subchannelTarget
|
90 | * @param channelArguments
|
91 | * @param channelCredentials
|
92 | */
|
93 | getOrCreateSubchannel(channelTargetUri, subchannelTarget, channelArguments, channelCredentials) {
|
94 | this.ensureCleanupTask();
|
95 | const channelTarget = uri_parser_1.uriToString(channelTargetUri);
|
96 | if (channelTarget in this.pool) {
|
97 | const subchannelObjArray = this.pool[channelTarget];
|
98 | for (const subchannelObj of subchannelObjArray) {
|
99 | if (subchannel_address_1.subchannelAddressEqual(subchannelTarget, subchannelObj.subchannelAddress) &&
|
100 | channel_options_1.channelOptionsEqual(channelArguments, subchannelObj.channelArguments) &&
|
101 | channelCredentials._equals(subchannelObj.channelCredentials)) {
|
102 | return subchannelObj.subchannel;
|
103 | }
|
104 | }
|
105 | }
|
106 | // If we get here, no matching subchannel was found
|
107 | const subchannel = new subchannel_1.Subchannel(channelTargetUri, subchannelTarget, channelArguments, channelCredentials);
|
108 | if (!(channelTarget in this.pool)) {
|
109 | this.pool[channelTarget] = [];
|
110 | }
|
111 | this.pool[channelTarget].push({
|
112 | subchannelAddress: subchannelTarget,
|
113 | channelArguments,
|
114 | channelCredentials,
|
115 | subchannel,
|
116 | });
|
117 | subchannel.ref();
|
118 | return subchannel;
|
119 | }
|
120 | }
|
121 | exports.SubchannelPool = SubchannelPool;
|
122 | const globalSubchannelPool = new SubchannelPool();
|
123 | /**
|
124 | * Get either the global subchannel pool, or a new subchannel pool.
|
125 | * @param global
|
126 | */
|
127 | function getSubchannelPool(global) {
|
128 | if (global) {
|
129 | return globalSubchannelPool;
|
130 | }
|
131 | else {
|
132 | return new SubchannelPool();
|
133 | }
|
134 | }
|
135 | exports.getSubchannelPool = getSubchannelPool;
|
136 | //# sourceMappingURL=subchannel-pool.js.map |
\ | No newline at end of file |