1
- use std:: sync:: Arc ;
2
-
3
1
use crate :: api:: status_new;
4
2
use crate :: job_queue:: build_queue;
5
3
use crate :: load:: SiteCtxt ;
4
+ use chrono:: { DateTime , Utc } ;
6
5
use database:: {
7
6
BenchmarkJob , BenchmarkJobStatus , BenchmarkRequest , BenchmarkRequestStatus ,
8
7
BenchmarkRequestType , Connection ,
9
8
} ;
10
9
use hashbrown:: HashMap ;
10
+ use std:: sync:: Arc ;
11
+ use std:: time:: Duration ;
11
12
12
13
pub async fn handle_status_page_new ( ctxt : Arc < SiteCtxt > ) -> anyhow:: Result < status_new:: Response > {
13
14
let conn = ctxt. conn ( ) . await ;
14
15
15
16
let index = conn. load_benchmark_request_index ( ) . await ?;
16
17
17
18
// The queue contains any in-progress request(s) and then the following requests in queue order
18
- // We reverse so that it starts with the request that will be benchmarked the latest
19
- let mut queue: Vec < status_new:: BenchmarkRequest > = build_queue ( & * conn, & index)
20
- . await ?
19
+ let queue = build_queue ( & * conn, & index) . await ?;
20
+ let completed = conn. get_last_n_completed_benchmark_requests ( 10 ) . await ?;
21
+
22
+ // Figure out approximately how long was the most recent master benchmark request
23
+ let expected_duration = completed
24
+ . iter ( )
25
+ . filter ( |req| req. request . is_master ( ) )
26
+ . filter_map ( |req| match req. request . status ( ) {
27
+ BenchmarkRequestStatus :: Completed { duration, .. } => Some ( duration) ,
28
+ _ => None ,
29
+ } )
30
+ . next ( )
31
+ . unwrap_or ( Duration :: from_secs ( 3600 ) ) ;
32
+
33
+ let in_progress_jobs = conn. get_jobs_of_in_progress_benchmark_requests ( ) . await ?;
34
+
35
+ // Here we compute the estimated end time for queued requests, and convert the requests to their
36
+ // frontend representation.
37
+ // We assume that at most a single request is in progress
38
+
39
+ let now = Utc :: now ( ) ;
40
+
41
+ // The estimated start time of the current in-progress request
42
+ let current_request_start = if let Some ( req) = queue. first ( ) . take_if ( |req| req. is_in_progress ( ) )
43
+ {
44
+ // Here we need to somehow guess when did the current in-progress request actually start,
45
+ // as we do not have that information readily available
46
+ let request_jobs = in_progress_jobs
47
+ . get ( req. tag ( ) . expect ( "In progress request without a tag" ) )
48
+ . map ( |jobs| jobs. as_slice ( ) )
49
+ . unwrap_or ( & [ ] ) ;
50
+
51
+ // Take the earliest start time, if some job has already started
52
+ // If there are no started jobs yet, just fall back to the current time (we guess that a
53
+ // job will start "any time now")
54
+ request_jobs
55
+ . iter ( )
56
+ . filter_map ( |job| match job. status ( ) {
57
+ BenchmarkJobStatus :: Queued => None ,
58
+ BenchmarkJobStatus :: InProgress { started_at, .. }
59
+ | BenchmarkJobStatus :: Completed { started_at, .. } => Some ( * started_at) ,
60
+ } )
61
+ . min ( )
62
+ . unwrap_or ( now)
63
+ } else {
64
+ // Assume that the next request (if any) will start at any given moment
65
+ now
66
+ } ;
67
+
68
+ // Estimate when the current in-progress request should end
69
+ // This ignores the fact that different kinds of requests (e.g. release ones) can have different
70
+ // durations, but these are rare and it's not worth the complexity to have multiple estimates
71
+ // here.
72
+ let current_request_end = current_request_start + expected_duration;
73
+
74
+ let mut requests: Vec < status_new:: BenchmarkRequest > = queue
21
75
. into_iter ( )
22
- . map ( |req| request_to_ui ( & req, HashMap :: new ( ) ) )
76
+ . enumerate ( )
77
+ . map ( |( index, req) | {
78
+ let estimated_end = if req. is_in_progress ( ) {
79
+ current_request_end
80
+ } else {
81
+ current_request_end + expected_duration * ( index as u32 )
82
+ } ;
83
+ request_to_ui ( & req, HashMap :: default ( ) , Some ( estimated_end) )
84
+ } )
23
85
. collect ( ) ;
24
- queue. reverse ( ) ;
25
- // And then we add N most recently completed requests to it
26
- let completed = conn. get_last_n_completed_benchmark_requests ( 10 ) . await ?;
27
- queue. extend (
86
+
87
+ // We reverse the queued requests so that they start with the request that will be benchmarked the latest
88
+ requests. reverse ( ) ;
89
+ // And then we add the completed requests
90
+ requests. extend (
28
91
completed
29
92
. into_iter ( )
30
- . map ( |req| request_to_ui ( & req. request , req. errors ) ) ,
93
+ . map ( |req| request_to_ui ( & req. request , req. errors , None ) ) ,
31
94
) ;
32
95
33
- let collectors = build_collectors ( conn. as_ref ( ) ) . await ?;
96
+ let collectors = build_collectors ( conn. as_ref ( ) , & in_progress_jobs ) . await ?;
34
97
35
98
Ok ( status_new:: Response {
36
- requests : queue ,
99
+ requests,
37
100
collectors,
38
101
} )
39
102
}
40
103
41
- async fn build_collectors ( conn : & dyn Connection ) -> anyhow:: Result < Vec < status_new:: Collector > > {
42
- let in_progress_jobs = conn. get_jobs_of_in_progress_benchmark_requests ( ) . await ?;
104
+ async fn build_collectors (
105
+ conn : & dyn Connection ,
106
+ in_progress_jobs : & HashMap < String , Vec < BenchmarkJob > > ,
107
+ ) -> anyhow:: Result < Vec < status_new:: Collector > > {
43
108
let collectors = conn. get_collector_configs ( ) . await ?;
44
109
let mut collector_map: HashMap < String , status_new:: Collector > = collectors
45
110
. into_iter ( )
@@ -120,11 +185,12 @@ fn job_status_to_priority(status: status_new::BenchmarkJobStatus) -> u32 {
120
185
fn request_to_ui (
121
186
req : & BenchmarkRequest ,
122
187
errors : HashMap < String , String > ,
188
+ estimated_end : Option < DateTime < Utc > > ,
123
189
) -> status_new:: BenchmarkRequest {
124
190
let ( completed_at, duration_s) = match req. status ( ) {
125
- BenchmarkRequestStatus :: WaitingForArtifacts => ( None , None ) ,
126
- BenchmarkRequestStatus :: ArtifactsReady => ( None , None ) ,
127
- BenchmarkRequestStatus :: InProgress => ( None , None ) ,
191
+ BenchmarkRequestStatus :: WaitingForArtifacts => ( estimated_end , None ) ,
192
+ BenchmarkRequestStatus :: ArtifactsReady => ( estimated_end , None ) ,
193
+ BenchmarkRequestStatus :: InProgress => ( estimated_end , None ) ,
128
194
BenchmarkRequestStatus :: Completed {
129
195
completed_at,
130
196
duration,
@@ -150,6 +216,7 @@ fn request_to_ui(
150
216
completed_at,
151
217
duration_s,
152
218
errors,
219
+ end_estimated : estimated_end. is_some ( ) ,
153
220
}
154
221
}
155
222
0 commit comments