11const got = ( ...args ) => import ( 'got' ) . then ( ( { default : got } ) => got ( ...args ) ) ;
2- const { Mutex} = require ( 'async-mutex' ) ;
2+ const { Mutex, withTimeout , E_TIMEOUT } = require ( 'async-mutex' ) ;
33const apiKeys = require ( './apiKey' ) ;
44const path = require ( "path" ) ;
55const logger = require ( '../logger' ) . logger ;
66
77const GET_LOCATIONS = 'http://www.webpagetest.org/getLocations.php?f=json' ;
88
99class 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