@@ -205,16 +205,6 @@ impl SubgraphTriggerProcessor {
205
205
}
206
206
}
207
207
208
- /// Try to extract deployment hash from the trigger context
209
- /// This is implementation-specific and may need adjustment
210
- fn try_extract_deployment < C : Blockchain > (
211
- & self ,
212
- _triggers : & [ HostedTrigger < ' _ , C > ] ,
213
- ) -> Option < DeploymentHash > {
214
- // This would need to be implemented based on your specific context
215
- // For now, return None to use fallback
216
- None
217
- }
218
208
219
209
/// Get comprehensive metrics for monitoring
220
210
pub async fn get_metrics ( & self ) -> HashMap < String , usize > {
@@ -322,16 +312,12 @@ where
322
312
return Ok ( state) ;
323
313
}
324
314
325
- // Try to extract deployment hash from the context
326
- // This is a best-effort approach - if we can't get it, fall back to data source name
327
- let deployment_hash = if let Some ( deployment) = self . try_extract_deployment ( & triggers) {
328
- deployment
329
- } else {
330
- // Fallback: create a synthetic deployment hash from data source
331
- let data_source_name = triggers. first ( ) . unwrap ( ) . host . data_source ( ) . name ( ) ;
332
- DeploymentHash :: new ( data_source_name)
333
- . unwrap_or_else ( |_| DeploymentHash :: new ( "unknown" ) . unwrap ( ) )
334
- } ;
315
+ // Create a synthetic deployment hash from data source name for consistent sharding.
316
+ // This ensures triggers from the same data source/subgraph are always routed to
317
+ // the same shard, maintaining cache locality.
318
+ let data_source_name = triggers[ 0 ] . host . data_source ( ) . name ( ) ;
319
+ let deployment_hash = DeploymentHash :: new ( data_source_name)
320
+ . unwrap_or_else ( |_| DeploymentHash :: new ( "unknown" ) . unwrap ( ) ) ;
335
321
336
322
// Determine shard assignment
337
323
let shard_id = if self . config . enable_sharding {
@@ -345,19 +331,19 @@ where
345
331
// Get subgraph state for backpressure
346
332
let subgraph_state = self . get_or_create_subgraph_state ( & deployment_hash) ;
347
333
348
- // Track queue depth
349
- let current_queue_depth = subgraph_state
334
+ // Check current queue depth before adding new triggers (avoid increment-then-check)
335
+ let current_queue_depth = subgraph_state. queue_depth . load ( Ordering :: Relaxed ) ;
336
+ let projected_queue_depth = current_queue_depth + triggers. len ( ) ;
337
+
338
+ // Apply backpressure if needed BEFORE incrementing queue depth
339
+ self . apply_backpressure ( logger, & deployment_hash, projected_queue_depth)
340
+ . await ;
341
+
342
+ // Only increment queue depth after backpressure check passes
343
+ subgraph_state
350
344
. queue_depth
351
345
. fetch_add ( triggers. len ( ) , Ordering :: Relaxed ) ;
352
346
353
- // Apply backpressure if needed
354
- self . apply_backpressure (
355
- logger,
356
- & deployment_hash,
357
- current_queue_depth + triggers. len ( ) ,
358
- )
359
- . await ;
360
-
361
347
debug ! ( logger, "Processing triggers" ;
362
348
"deployment" => deployment_hash. to_string( ) ,
363
349
"shard" => shard_id,
@@ -367,59 +353,78 @@ where
367
353
368
354
proof_of_indexing. start_handler ( causality_region) ;
369
355
370
- for HostedTrigger {
371
- host,
372
- mapping_trigger,
373
- } in triggers
374
- {
375
- // Acquire permit and hold it during processing
376
- let permit = semaphore. acquire ( ) . await . unwrap ( ) ;
377
-
378
- // Track active permits
379
- self . shard_metrics [ shard_id]
380
- . active_permits
381
- . fetch_add ( 1 , Ordering :: Relaxed ) ;
382
-
383
- let start = Instant :: now ( ) ;
384
-
385
- // Process with permit held
386
- state = host
387
- . process_mapping_trigger (
388
- logger,
389
- mapping_trigger,
390
- state,
391
- proof_of_indexing. cheap_clone ( ) ,
392
- debug_fork,
393
- instrument,
394
- )
395
- . await ?;
396
-
397
- // Permit is automatically dropped here, releasing it
398
- drop ( permit) ;
399
-
400
- // Update metrics
401
- self . shard_metrics [ shard_id]
402
- . active_permits
403
- . fetch_sub ( 1 , Ordering :: Relaxed ) ;
404
- self . shard_metrics [ shard_id]
405
- . total_processed
406
- . fetch_add ( 1 , Ordering :: Relaxed ) ;
407
-
408
- // Decrement queue depth after processing
409
- subgraph_state. queue_depth . fetch_sub ( 1 , Ordering :: Relaxed ) ;
410
-
411
- let elapsed = start. elapsed ( ) ;
412
- subgraph_metrics. observe_trigger_processing_duration ( elapsed. as_secs_f64 ( ) ) ;
413
-
414
- if elapsed > Duration :: from_secs ( 30 ) {
415
- debug ! ( logger, "Trigger processing took a long time" ;
416
- "duration_ms" => elapsed. as_millis( ) ,
417
- "shard" => shard_id
418
- ) ;
356
+ // Track processed triggers to ensure proper queue depth cleanup
357
+ let mut processed_count = 0 ;
358
+
359
+ // Use a closure to ensure queue depth is properly decremented on any exit path
360
+ let process_result = async {
361
+ for HostedTrigger {
362
+ host,
363
+ mapping_trigger,
364
+ } in triggers
365
+ {
366
+ // Acquire permit and hold it during processing
367
+ let permit = semaphore. acquire ( ) . await . unwrap ( ) ;
368
+
369
+ // Track active permits
370
+ self . shard_metrics [ shard_id]
371
+ . active_permits
372
+ . fetch_add ( 1 , Ordering :: Relaxed ) ;
373
+
374
+ let start = Instant :: now ( ) ;
375
+
376
+ // Process with permit held
377
+ let result = host
378
+ . process_mapping_trigger (
379
+ logger,
380
+ mapping_trigger,
381
+ state,
382
+ proof_of_indexing. cheap_clone ( ) ,
383
+ debug_fork,
384
+ instrument,
385
+ )
386
+ . await ;
387
+
388
+ // Permit is automatically dropped here, releasing it
389
+ drop ( permit) ;
390
+
391
+ // Update metrics
392
+ self . shard_metrics [ shard_id]
393
+ . active_permits
394
+ . fetch_sub ( 1 , Ordering :: Relaxed ) ;
395
+ self . shard_metrics [ shard_id]
396
+ . total_processed
397
+ . fetch_add ( 1 , Ordering :: Relaxed ) ;
398
+
399
+ // Increment processed count for queue cleanup
400
+ processed_count += 1 ;
401
+
402
+ // Handle result
403
+ state = result?;
404
+
405
+ let elapsed = start. elapsed ( ) ;
406
+ subgraph_metrics. observe_trigger_processing_duration ( elapsed. as_secs_f64 ( ) ) ;
407
+
408
+ if elapsed > Duration :: from_secs ( 30 ) {
409
+ debug ! ( logger, "Trigger processing took a long time" ;
410
+ "duration_ms" => elapsed. as_millis( ) ,
411
+ "shard" => shard_id
412
+ ) ;
413
+ }
419
414
}
415
+ Ok ( state)
416
+ } ;
417
+
418
+ // Execute processing and ensure queue depth cleanup regardless of outcome
419
+ let result = process_result. await ;
420
+
421
+ // Always decrement queue depth by the number of processed triggers
422
+ // This ensures cleanup even if processing failed partway through
423
+ if processed_count > 0 {
424
+ subgraph_state. queue_depth . fetch_sub ( processed_count, Ordering :: Relaxed ) ;
420
425
}
421
426
422
- Ok ( state )
427
+ result
423
428
}
424
429
}
425
430
0 commit comments