1
1
import _ from 'lodash' ;
2
- import type { RemoteProbeSocket } from '../lib/ws/server.js' ;
3
2
import { fetchSockets } from '../lib/ws/fetch-sockets.js' ;
4
- import type { LocationWithLimit } from '../measurement/types.js' ;
3
+ import type { LocationWithLimit , MeasurementRecord } from '../measurement/types.js' ;
5
4
import type { Location } from '../lib/location/types.js' ;
6
5
import type { Probe } from './types.js' ;
7
- import { SocketsLocationFilter } from './sockets -location-filter.js' ;
6
+ import { ProbesLocationFilter } from './probes -location-filter.js' ;
8
7
import { getMeasurementStore } from '../measurement/store.js' ;
9
8
10
9
export class ProbeRouter {
11
- private readonly socketsFilter = new SocketsLocationFilter ( ) ;
10
+ private readonly probesFilter = new ProbesLocationFilter ( ) ;
12
11
13
12
private readonly store = getMeasurementStore ( ) ;
14
13
@@ -18,51 +17,51 @@ export class ProbeRouter {
18
17
locations : LocationWithLimit [ ] | string = [ ] ,
19
18
globalLimit = 1 ,
20
19
) : Promise < Probe [ ] > {
21
- const sockets = await this . fetchSockets ( ) ;
22
- let filtered : RemoteProbeSocket [ ] = [ ] ;
20
+ const probes = await this . fetchProbes ( ) ;
21
+ let filtered : Probe [ ] = [ ] ;
23
22
24
23
if ( typeof locations === 'string' ) {
25
- filtered = await this . findWithMeasurementId ( sockets , locations ) ;
24
+ filtered = await this . findWithMeasurementId ( probes , locations ) ;
26
25
} else if ( locations . some ( l => l . limit ) ) {
27
- filtered = this . findWithLocationLimit ( sockets , locations ) ;
26
+ filtered = this . findWithLocationLimit ( probes , locations ) ;
28
27
} else if ( locations . length > 0 ) {
29
- filtered = this . findWithGlobalLimit ( sockets , locations , globalLimit ) ;
28
+ filtered = this . findWithGlobalLimit ( probes , locations , globalLimit ) ;
30
29
} else {
31
- filtered = this . findGloballyDistributed ( sockets , globalLimit ) ;
30
+ filtered = this . findGloballyDistributed ( probes , globalLimit ) ;
32
31
}
33
32
34
- return filtered . map ( s => s . data . probe ) ;
33
+ return filtered ;
35
34
}
36
35
37
- private async fetchSockets ( ) : Promise < RemoteProbeSocket [ ] > {
36
+ private async fetchProbes ( ) : Promise < Probe [ ] > {
38
37
const sockets = await this . fetchWsSockets ( ) ;
39
- return sockets . filter ( s => s . data . probe . status === 'ready' ) ;
38
+ return sockets . filter ( s => s . data . probe . status === 'ready' ) . map ( s => s . data . probe ) ;
40
39
}
41
40
42
- private findGloballyDistributed ( sockets : RemoteProbeSocket [ ] , limit : number ) : RemoteProbeSocket [ ] {
43
- return this . socketsFilter . filterGloballyDistibuted ( sockets , limit ) ;
41
+ private findGloballyDistributed ( probes : Probe [ ] , limit : number ) : Probe [ ] {
42
+ return this . probesFilter . filterGloballyDistibuted ( probes , limit ) ;
44
43
}
45
44
46
- private findWithGlobalLimit ( sockets : RemoteProbeSocket [ ] , locations : Location [ ] , limit : number ) : RemoteProbeSocket [ ] {
45
+ private findWithGlobalLimit ( probes : Probe [ ] , locations : Location [ ] , limit : number ) : Probe [ ] {
47
46
const weight = Math . floor ( 100 / locations . length ) ;
48
47
const distribution = new Map ( locations . map ( l => [ l , weight ] ) ) ;
49
48
50
- return this . socketsFilter . filterByLocationAndWeight ( sockets , distribution , limit ) ;
49
+ return this . probesFilter . filterByLocationAndWeight ( probes , distribution , limit ) ;
51
50
}
52
51
53
- private findWithLocationLimit ( sockets : RemoteProbeSocket [ ] , locations : LocationWithLimit [ ] ) : RemoteProbeSocket [ ] {
54
- const grouped = new Map < LocationWithLimit , RemoteProbeSocket [ ] > ( ) ;
52
+ private findWithLocationLimit ( probes : Probe [ ] , locations : LocationWithLimit [ ] ) : Probe [ ] {
53
+ const grouped = new Map < LocationWithLimit , Probe [ ] > ( ) ;
55
54
56
55
for ( const location of locations ) {
57
56
const { limit, ...l } = location ;
58
- const found = this . socketsFilter . filterByLocation ( sockets , l ) ;
57
+ const found = this . probesFilter . filterByLocation ( probes , l ) ;
59
58
60
59
if ( found . length > 0 ) {
61
60
grouped . set ( location , found ) ;
62
61
}
63
62
}
64
63
65
- const picked = new Set < RemoteProbeSocket > ( ) ;
64
+ const picked = new Set < Probe > ( ) ;
66
65
67
66
for ( const [ loc , soc ] of grouped ) {
68
67
for ( const s of _ . take ( soc , loc . limit ) ) {
@@ -73,9 +72,68 @@ export class ProbeRouter {
73
72
return [ ...picked ] ;
74
73
}
75
74
76
- private async findWithMeasurementId ( sockets : RemoteProbeSocket [ ] , measurementId : string ) : Promise < RemoteProbeSocket [ ] > {
77
- const ips = await this . store . getIpsByMeasurementId ( measurementId ) ;
78
- return sockets . filter ( socket => ips . includes ( socket . data . probe . ipAddress ) ) ;
75
+ private async findWithMeasurementId ( probes : Probe [ ] , measurementId : string ) : Promise < Probe [ ] > {
76
+ let prevMeasurement : MeasurementRecord | undefined ;
77
+ const prevIps = await this . store . getIpsByMeasurementId ( measurementId ) ;
78
+ const ipToProbe = new Map ( probes . map ( probe => [ probe . ipAddress , probe ] ) ) ;
79
+ const result : Probe [ ] = [ ] ;
80
+
81
+ for ( let i = 0 ; i < prevIps . length ; i ++ ) {
82
+ const ip = prevIps [ i ] ! ;
83
+ const probe = ipToProbe . get ( ip ) ;
84
+
85
+ if ( probe ) {
86
+ result . push ( probe ) ;
87
+ } else {
88
+ if ( ! prevMeasurement ) {
89
+ prevMeasurement = await this . store . getMeasurementJson ( measurementId ) ;
90
+
91
+ if ( ! prevMeasurement ) { return [ ] ; }
92
+ }
93
+
94
+ const prevTest = prevMeasurement . results [ i ] ;
95
+
96
+ if ( ! prevTest ) { return [ ] ; }
97
+
98
+ const offlineProbe : Probe = {
99
+ status : 'offline' ,
100
+ client : '' ,
101
+ version : '' ,
102
+ nodeVersion : '' ,
103
+ uuid : '' ,
104
+ isHardware : false ,
105
+ hardwareDevice : null ,
106
+ ipAddress : ip ,
107
+ host : '' ,
108
+ location : {
109
+ continent : prevTest . probe . continent ,
110
+ region : prevTest . probe . region ,
111
+ country : prevTest . probe . country ,
112
+ city : prevTest . probe . city ,
113
+ normalizedCity : prevTest . probe . city . toLowerCase ( ) ,
114
+ asn : prevTest . probe . asn ,
115
+ latitude : prevTest . probe . latitude ,
116
+ longitude : prevTest . probe . longitude ,
117
+ state : prevTest . probe . state ?? undefined ,
118
+ network : prevTest . probe . network ,
119
+ normalizedNetwork : prevTest . probe . network . toLowerCase ( ) ,
120
+ } ,
121
+ index : [ ] ,
122
+ resolvers : prevTest . probe . resolvers ,
123
+ tags : prevTest . probe . tags . map ( tag => ( { value : tag , type : 'system' } ) ) ,
124
+ stats : {
125
+ cpu : {
126
+ count : 0 ,
127
+ load : [ ] ,
128
+ } ,
129
+ jobs : { count : 0 } ,
130
+ } ,
131
+ } ;
132
+ result . push ( offlineProbe ) ;
133
+ }
134
+ }
135
+
136
+ return result ;
79
137
}
80
138
}
81
139
0 commit comments