Skip to content

Commit 2fff543

Browse files
- improved selection algorithm
- introduced locations update timeout
1 parent a15f154 commit 2fff543

File tree

1 file changed

+47
-15
lines changed

1 file changed

+47
-15
lines changed

wtp/locationSelector.js

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
const got = (...args) => import('got').then(({default: got}) => got(...args));
2-
const {Mutex} = require('async-mutex');
2+
const {Mutex, withTimeout, E_TIMEOUT} = require('async-mutex');
33
const apiKeys = require('./apiKey');
44
const path = require("path");
55
const logger = require('../logger').logger;
66

77
const GET_LOCATIONS = 'http://www.webpagetest.org/getLocations.php?f=json';
88

99
class LocationSelector {
10-
CACHE_TTL = 10;
10+
CACHE_TTL = 0;
1111
DEFAULT_LOCATION = 'IAD_US_01';
12+
UPDATE_LOCATIONS_TIMEOUT = 20;
1213

1314
constructor() {
1415
if (!LocationSelector.instance) {
1516
this.cachedAllLocations = [];
1617
this.location = this.DEFAULT_LOCATION;
1718
this.lastUpdated = null;
18-
this.mutex = new Mutex();
19+
this.mutex = withTimeout(new Mutex(), this.UPDATE_LOCATIONS_TIMEOUT * 1000);
1920
LocationSelector.instance = this;
2021
}
2122
return LocationSelector.instance;
@@ -50,19 +51,42 @@ class LocationSelector {
5051
}
5152
};
5253

53-
getLocationScore(location) {
54-
let metrics = location.PendingTests;
55-
56-
// Idle + Testing ==> capacity
57-
if (metrics.Idle + metrics.Testing == 0) {
58-
return 1; // no instances running, hopefully they will be spin up for our request?
54+
getLocationScore(loc) {
55+
// no instances running, hopefully they will be spin up for our request?
56+
if (this.getLocationCapacity(loc) == 0) {
57+
return 1;
5958
}
6059

60+
let metrics = loc.PendingTests;
6161
return (metrics.HighPriority + metrics.Testing) / (metrics.Idle + metrics.Testing)
6262
}
6363

64+
getLocationCapacity(loc) {
65+
return loc.PendingTests.Idle + loc.PendingTests.Testing;
66+
}
67+
6468
getBestLocationId(locations) {
65-
let selected = locations.reduce((acc, cur) => acc && acc.score < cur.score ? acc : cur);
69+
let selected = locations.reduce((acc, cur) => {
70+
// if nothing to compare to, use current value
71+
if (!acc) {
72+
return cur;
73+
}
74+
75+
// if acc less loaded
76+
if (acc.score < cur.score) {
77+
return acc;
78+
}
79+
80+
// if cur less loaded
81+
if (acc.score > cur.score) {
82+
return cur;
83+
}
84+
85+
// if same load on acc and cur
86+
// then choose the one with bigger capacity (Idle + Testing)
87+
return this.getLocationCapacity(acc) > this.getLocationCapacity(cur) ? acc : cur;
88+
});
89+
6690
return selected.location;
6791
}
6892

@@ -83,7 +107,9 @@ class LocationSelector {
83107
}
84108

85109
// enrich locations with our internal score
86-
filtered.forEach((location) => { location.score = this.getLocationScore(location); });
110+
filtered.forEach((loc) => {
111+
loc.score = this.getLocationScore(loc);
112+
});
87113

88114
this.location = this.getBestLocationId(filtered);
89115
this.cachedAllLocations = filtered;
@@ -92,11 +118,17 @@ class LocationSelector {
92118

93119
async getLocation() {
94120
if (this.isExpired()) {
95-
await this.mutex.runExclusive(async () => {
96-
if (this.isExpired()) {
97-
await this.updateLocations();
121+
try {
122+
await this.mutex.runExclusive(async () => {
123+
if (this.isExpired()) {
124+
await this.updateLocations();
125+
}
126+
});
127+
} catch(e) {
128+
if (e === E_TIMEOUT) {
129+
logger.error('Locations update is taking too long', e);
98130
}
99-
});
131+
}
100132
}
101133

102134
return this.location;

0 commit comments

Comments
 (0)