This repository has been archived by the owner on Sep 7, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
target_allocation.js
124 lines (108 loc) · 3.95 KB
/
target_allocation.js
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
// Copyright 2018 Google LLC
//
// 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.
var TargetAllocation = module.exports = function(proxy) {
this.proxy = proxy;
this.maxTargets = Number(process.env.TARGETS_PER_TENANT) || 2; // max number of targets allocated per tenant
this.serverIndexes = {}; // { <tenantId>: Int }
this.pending = {}; // <tenantId> : [cb]
};
// lookup target for tenantid, try to allocate if none are available
TargetAllocation.prototype.lookup = function(tenantId, callback) {
var self = this;
// only allow 1 pending lookup/allocation req per tenantId
if (!this.pending.hasOwnProperty(tenantId)) {
this.pending[tenantId] = [callback];
this._lookup(tenantId, this.maxTargets, function(err, target) {
self.pending[tenantId].forEach(function(cb) {
// make sure cb is called in the next event loop, so self.pending[tenantId] is removed
setImmediate(function() {
cb(err, target);;
});
});
delete self.pending[tenantId];
});
} else {
this.pending[tenantId].push(callback)
}
};
TargetAllocation.prototype._lookup = function(tenantId, maxTargets, callback) {
var self = this;
var servers = self.proxy.targets(tenantId);
if (!this.serverIndexes.hasOwnProperty(tenantId)) {
this.serverIndexes[tenantId] = 0;
}
if (servers.length < maxTargets) {
this.allocate(tenantId, function(err) {
if (err) {
// handle case where faild to allocate any more targets, but we have at least one allocated
if (servers.length > 0) {
return self._lookup(tenantId, servers.length, callback);
} else {
return callback(err);
}
}
return self._lookup(tenantId, maxTargets, callback);
});
return;
} else {
var server = servers[this.serverIndexes[tenantId]++ % servers.length];
return callback(null, server.url);
}
};
TargetAllocation.prototype.allocate = function(tenantId, callback) {
var self = this;
// query service reg for all targets
this.proxy._serviceRegistryClient.findAll(function(err, results) {
if (err) {
return callback(err);
}
if (!results) {
return callback(new Error('No available target servers for tenant `' + tenantId + '`.'));
}
var allocated = results.filter(function(server) {
if (server.tenantId === tenantId && server.version === self.proxy._currentVersion) {
// filter by online targets
return self.proxy._targetMonitor.status(server.url);
}
});
if (allocated.length >= self.maxTargets) {
// proxy._severs isn't up to date, force update proxy._servers
self.proxy._processServerList(results);
return callback();
}
var unallocated = results.filter(function(server) {
return !server.tenantId && server.version === self.proxy._currentVersion
});
var target = unallocated.shift();
if (!target) {
return callback(new Error('No available target servers for tenant `' + tenantId + '`.'));
}
var newRecord = {
url: target.url,
tenantId: tenantId,
created: target.created,
version: target.version
};
self.proxy._serviceRegistryClient.allocate('cloud-target', target, newRecord, function(err) {
if (err) {
return callback(err);
};
if (!self.proxy._servers[tenantId]) {
self.proxy._servers[tenantId] = [];
}
self.proxy._servers[tenantId].push(newRecord);
return callback();
});
});
};