@@ -8,6 +8,7 @@ use axum::extract::Json;
8
8
use axum:: extract:: Path ;
9
9
use axum:: extract:: State ;
10
10
use axum:: Extension ;
11
+ use derivative:: Derivative ;
11
12
use editoast_authz:: BuiltinRole ;
12
13
use editoast_common:: units;
13
14
use editoast_schemas:: rolling_stock:: LoadingGaugeType ;
@@ -144,12 +145,16 @@ impl From<PathfindingCoreResult> for PathfindingResult {
144
145
}
145
146
}
146
147
147
- #[ derive( Serialize , Deserialize , Clone , Debug , PartialEq , ToSchema ) ]
148
+ #[ derive( Serialize , Deserialize , Clone , Debug , PartialEq , ToSchema , Derivative ) ]
149
+ #[ derivative( Default ) ]
148
150
#[ serde( tag = "failed_status" , rename_all = "snake_case" ) ]
149
151
pub enum PathfindingFailure {
150
152
PathfindingInputError ( PathfindingInputError ) ,
153
+ #[ derivative( Default ) ]
151
154
PathfindingNotFound ( PathfindingNotFound ) ,
152
- InternalError { core_error : InternalError } ,
155
+ InternalError {
156
+ core_error : InternalError ,
157
+ } ,
153
158
}
154
159
155
160
/// Compute a pathfinding
@@ -215,18 +220,37 @@ async fn pathfinding_blocks_batch(
215
220
infra : & Infra ,
216
221
pathfinding_inputs : & [ PathfindingInput ] ,
217
222
) -> Result < Vec < PathfindingResult > > {
223
+ let mut hash_to_path_indexes: HashMap < String , Vec < usize > > = HashMap :: default ( ) ;
224
+ let mut path_request_map: HashMap < String , PathfindingInput > = HashMap :: default ( ) ;
225
+ let mut pathfinding_results =
226
+ vec ! [ PathfindingResult :: Failure ( PathfindingFailure :: default ( ) ) ; pathfinding_inputs. len( ) ] ;
227
+ for ( index, path_input) in pathfinding_inputs. iter ( ) . enumerate ( ) {
228
+ let pathfinding_hash = path_input_hash ( infra. id , & infra. version , path_input) ;
229
+ hash_to_path_indexes
230
+ . entry ( pathfinding_hash. clone ( ) )
231
+ . or_default ( )
232
+ . push ( index) ;
233
+ path_request_map
234
+ . entry ( pathfinding_hash. clone ( ) )
235
+ . or_insert ( path_input. clone ( ) ) ;
236
+ }
237
+
238
+ info ! (
239
+ nb_pathfindings = pathfinding_inputs. len( ) ,
240
+ nb_unique_pathfindings = hash_to_path_indexes. len( )
241
+ ) ;
242
+
218
243
// Compute hashes of all path_inputs
219
- let hashes: Vec < _ > = pathfinding_inputs
220
- . iter ( )
221
- . map ( |input| path_input_hash ( infra. id , & infra. version , input) )
222
- . collect ( ) ;
244
+ let hashes = hash_to_path_indexes. keys ( ) . collect :: < Vec < _ > > ( ) ;
223
245
224
246
// Try to retrieve the result from Valkey
225
- let mut pathfinding_results : Vec < Option < PathfindingResult > > =
247
+ let pathfinding_cached_results : Vec < Option < PathfindingResult > > =
226
248
valkey_conn. json_get_bulk ( & hashes) . await ?;
249
+ let pathfinding_cached_results: HashMap < _ , _ > =
250
+ hashes. into_iter ( ) . zip ( pathfinding_cached_results) . collect ( ) ;
227
251
228
252
// Report number of hit cache
229
- let nb_hit = pathfinding_results . iter ( ) . flatten ( ) . count ( ) ;
253
+ let nb_hit = pathfinding_cached_results . values ( ) . flatten ( ) . count ( ) ;
230
254
info ! (
231
255
nb_hit,
232
256
nb_miss = pathfinding_inputs. len( ) - nb_hit,
@@ -235,11 +259,10 @@ async fn pathfinding_blocks_batch(
235
259
236
260
// Handle miss cache:
237
261
debug ! ( "Extracting locations from path items" ) ;
238
- let path_items: Vec < _ > = pathfinding_results
262
+ let path_items: Vec < _ > = pathfinding_cached_results
239
263
. iter ( )
240
- . zip ( pathfinding_inputs)
241
- . filter ( |( res, _) | res. is_none ( ) )
242
- . flat_map ( |( _, input) | & input. path_items )
264
+ . filter ( |( _, res) | res. is_none ( ) )
265
+ . flat_map ( |( hash, _) | & path_request_map[ * hash] . path_items )
243
266
. collect ( ) ;
244
267
let path_item_cache = PathItemCache :: load ( conn, infra. id , & path_items) . await ?;
245
268
@@ -249,24 +272,25 @@ async fn pathfinding_blocks_batch(
249
272
) ;
250
273
let mut to_cache = vec ! [ ] ;
251
274
let mut pathfinding_requests = vec ! [ ] ;
252
- let mut pathfinding_requests_index = vec ! [ ] ;
253
- for ( index, ( pathfinding_result, pathfinding_input) ) in pathfinding_results
254
- . iter_mut ( )
255
- . zip ( pathfinding_inputs)
256
- . enumerate ( )
257
- {
258
- if pathfinding_result. is_some ( ) {
275
+ let mut to_compute_hashes = vec ! [ ] ;
276
+ for ( hash, pathfinding_result) in pathfinding_cached_results. into_iter ( ) {
277
+ if let Some ( result) = pathfinding_result {
278
+ hash_to_path_indexes[ hash]
279
+ . iter ( )
280
+ . for_each ( |index| pathfinding_results[ * index] = result. clone ( ) ) ;
259
281
continue ;
260
282
}
261
-
283
+ let pathfinding_input = & path_request_map [ hash ] ;
262
284
match build_pathfinding_request ( pathfinding_input, infra, & path_item_cache) {
263
285
Ok ( pathfinding_request) => {
264
286
pathfinding_requests. push ( pathfinding_request) ;
265
- pathfinding_requests_index . push ( index ) ;
287
+ to_compute_hashes . push ( hash ) ;
266
288
}
267
289
Err ( result) => {
268
- * pathfinding_result = Some ( result. clone ( ) ) ;
269
- to_cache. push ( ( & hashes[ index] , result) ) ;
290
+ hash_to_path_indexes[ hash]
291
+ . iter ( )
292
+ . for_each ( |index| pathfinding_results[ * index] = result. clone ( ) ) ;
293
+ to_cache. push ( ( hash, result) ) ;
270
294
}
271
295
}
272
296
}
@@ -284,25 +308,26 @@ async fn pathfinding_blocks_batch(
284
308
. into_iter ( )
285
309
. collect ( ) ;
286
310
287
- for ( index, path_result) in computed_paths. into_iter ( ) . enumerate ( ) {
288
- let path_index = pathfinding_requests_index[ index] ;
289
- let path = match path_result {
311
+ for ( path_result, hash) in computed_paths. into_iter ( ) . zip ( to_compute_hashes) {
312
+ let result = match path_result {
290
313
Ok ( path) => {
291
- to_cache. push ( ( & hashes [ path_index ] , path. clone ( ) . into ( ) ) ) ;
314
+ to_cache. push ( ( hash , path. clone ( ) . into ( ) ) ) ;
292
315
path. into ( )
293
316
}
294
317
// TODO: only make HTTP status code errors non-fatal
295
318
Err ( core_error) => {
296
319
PathfindingResult :: Failure ( PathfindingFailure :: InternalError { core_error } )
297
320
}
298
321
} ;
299
- pathfinding_results[ path_index] = Some ( path) ;
322
+ hash_to_path_indexes[ hash]
323
+ . iter ( )
324
+ . for_each ( |index| pathfinding_results[ * index] = result. clone ( ) ) ;
300
325
}
301
326
302
- debug ! ( nb_results = to_cache. len( ) , "Caching pathfinding response" ) ;
327
+ debug ! ( nb_cached = to_cache. len( ) , "Caching pathfinding response" ) ;
303
328
valkey_conn. json_set_bulk ( & to_cache) . await ?;
304
329
305
- Ok ( pathfinding_results. into_iter ( ) . flatten ( ) . collect ( ) )
330
+ Ok ( pathfinding_results)
306
331
}
307
332
308
333
fn build_pathfinding_request (
0 commit comments