@@ -10,9 +10,12 @@ use crate::{
10
10
server:: context:: ServiceRunContext ,
11
11
service:: { Error , ImporterService } ,
12
12
} ;
13
- use std:: { path:: PathBuf , time:: Duration } ;
13
+ use std:: { collections :: HashMap , path:: PathBuf , time:: Duration } ;
14
14
use time:: OffsetDateTime ;
15
- use tokio:: time:: MissedTickBehavior ;
15
+ use tokio:: {
16
+ task:: { spawn_local, JoinHandle } ,
17
+ time:: MissedTickBehavior ,
18
+ } ;
16
19
use tracing:: instrument;
17
20
use trustify_common:: db:: Database ;
18
21
use trustify_module_analysis:: service:: AnalysisService ;
@@ -65,24 +68,45 @@ impl Server {
65
68
#[ instrument( skip_all, ret) ]
66
69
async fn run ( & self ) -> anyhow:: Result < ( ) > {
67
70
let service = ImporterService :: new ( self . db . clone ( ) ) ;
68
-
71
+ let runner = ImportRunner {
72
+ db : self . db . clone ( ) ,
73
+ storage : self . storage . clone ( ) ,
74
+ working_dir : self . working_dir . clone ( ) ,
75
+ analysis : self . analysis . clone ( ) ,
76
+ } ;
69
77
self . reset_all_jobs ( & service) . await ?;
70
78
71
79
let mut interval = tokio:: time:: interval ( Duration :: from_secs ( 1 ) ) ;
72
80
interval. set_missed_tick_behavior ( MissedTickBehavior :: Skip ) ;
73
81
82
+ // Maintain a list of currently running jobs
83
+ let mut runs = HashMap :: < String , JoinHandle < _ > > :: default ( ) ;
84
+
74
85
loop {
75
86
interval. tick ( ) . await ;
76
- futures:: future:: join_all (
77
- service
78
- . list ( )
79
- . await ?
80
- . into_iter ( )
81
- . filter ( |i| !( i. data . configuration . disabled || can_wait ( i) ) )
82
- . take ( self . concurrency )
83
- . map ( |i| self . import ( i, & service) ) ,
84
- )
85
- . await ;
87
+
88
+ // Remove jobs that are finished
89
+ runs. retain ( |_, job| !job. is_finished ( ) ) ;
90
+
91
+ // Asynchronously fire off new jobs subject to max concurrency
92
+ let todo: Vec < _ > = service
93
+ . list ( )
94
+ . await ?
95
+ . into_iter ( )
96
+ . filter ( |i| {
97
+ !( runs. contains_key ( & i. name ) || i. data . configuration . disabled || can_wait ( i) )
98
+ } )
99
+ . take ( self . concurrency - runs. len ( ) )
100
+ . map ( |i| {
101
+ (
102
+ i. name . clone ( ) ,
103
+ spawn_local ( import ( runner. clone ( ) , i, service. clone ( ) ) ) ,
104
+ )
105
+ } )
106
+ . collect ( ) ;
107
+
108
+ // Add them to our list of currently running jobs
109
+ runs. extend ( todo. into_iter ( ) ) ;
86
110
}
87
111
}
88
112
@@ -116,65 +140,61 @@ impl Server {
116
140
117
141
Ok ( ( ) )
118
142
}
143
+ }
119
144
120
- async fn import ( & self , importer : Importer , service : & ImporterService ) -> Result < ( ) , Error > {
121
- log:: debug!( " {}: {:?}" , importer. name, importer. data. configuration) ;
122
-
123
- service. update_start ( & importer. name , None ) . await ?;
145
+ async fn import (
146
+ runner : ImportRunner ,
147
+ importer : Importer ,
148
+ service : ImporterService ,
149
+ ) -> Result < ( ) , Error > {
150
+ log:: debug!( " {}: {:?}" , importer. name, importer. data. configuration) ;
124
151
125
- // record timestamp before processing, so that we can use it as "since" marker
126
- let last_run = OffsetDateTime :: now_utc ( ) ;
152
+ service. update_start ( & importer. name , None ) . await ?;
127
153
128
- log:: info!( "Starting run: {}" , importer. name) ;
154
+ // record timestamp before processing, so that we can use it as "since" marker
155
+ let last_run = OffsetDateTime :: now_utc ( ) ;
129
156
130
- let context = ServiceRunContext :: new ( service . clone ( ) , importer. name . clone ( ) ) ;
157
+ log :: info! ( "Starting run: {}" , importer. name) ;
131
158
132
- let runner = ImportRunner {
133
- db : self . db . clone ( ) ,
134
- storage : self . storage . clone ( ) ,
135
- working_dir : self . working_dir . clone ( ) ,
136
- analysis : self . analysis . clone ( ) ,
137
- } ;
159
+ let context = ServiceRunContext :: new ( service. clone ( ) , importer. name . clone ( ) ) ;
138
160
139
- let ( last_error, report, continuation) = match runner
140
- . run_once (
141
- context,
142
- importer. data . configuration ,
143
- importer. data . last_success ,
144
- importer. data . continuation ,
145
- )
146
- . await
147
- {
148
- Ok ( RunOutput {
161
+ let ( last_error, report, continuation) = match runner
162
+ . run_once (
163
+ context,
164
+ importer. data . configuration ,
165
+ importer. data . last_success ,
166
+ importer. data . continuation ,
167
+ )
168
+ . await
169
+ {
170
+ Ok ( RunOutput {
171
+ report,
172
+ continuation,
173
+ } ) => ( None , Some ( report) , continuation) ,
174
+ Err ( ScannerError :: Normal {
175
+ err,
176
+ output : RunOutput {
149
177
report,
150
178
continuation,
151
- } ) => ( None , Some ( report) , continuation) ,
152
- Err ( ScannerError :: Normal {
153
- err,
154
- output :
155
- RunOutput {
156
- report,
157
- continuation,
158
- } ,
159
- } ) => ( Some ( err. to_string ( ) ) , Some ( report) , continuation) ,
160
- Err ( ScannerError :: Critical ( err) ) => ( Some ( err. to_string ( ) ) , None , None ) ,
161
- } ;
179
+ } ,
180
+ } ) => ( Some ( err. to_string ( ) ) , Some ( report) , continuation) ,
181
+ Err ( ScannerError :: Critical ( err) ) => ( Some ( err. to_string ( ) ) , None , None ) ,
182
+ } ;
162
183
163
- log:: info!( "Import run complete: {last_error:?}" ) ;
184
+ log:: info!( "Import run complete: {last_error:?}" ) ;
164
185
165
- service
166
- . update_finish (
167
- & importer. name ,
168
- None ,
169
- last_run,
170
- last_error,
171
- continuation,
172
- report. and_then ( |report| serde_json:: to_value ( report) . ok ( ) ) ,
173
- )
174
- . await ?;
186
+ service
187
+ . update_finish (
188
+ & importer. name ,
189
+ None ,
190
+ last_run,
191
+ last_error,
192
+ continuation,
193
+ report. and_then ( |report| serde_json:: to_value ( report) . ok ( ) ) ,
194
+ )
195
+ . await ?;
175
196
176
- Ok ( ( ) )
177
- }
197
+ Ok ( ( ) )
178
198
}
179
199
180
200
/// check if we need to run or skip the importer
0 commit comments