@@ -560,12 +560,13 @@ async fn health_handler(State(state): State<AppState>) -> Json<serde_json::Value
560560 } ) )
561561}
562562
563- /// Debug endpoint — polls Iggy HTTP API directly and returns raw results.
563+ /// Debug endpoint — comprehensive Iggy diagnostics.
564+ /// Checks topic stats, scans all partitions, reports which have data.
564565async fn debug_iggy_handler ( ) -> Json < serde_json:: Value > {
565566 let iggy_http_url = env:: var ( "IGGY_HTTP_URL" ) . unwrap_or_else ( |_| "http://127.0.0.1:3000" . into ( ) ) ;
566567 let stream = env:: var ( "IGGY_STREAM" ) . unwrap_or_else ( |_| "tracker" . into ( ) ) ;
567- let topic = env:: var ( "IGGY_TOPIC" ) . unwrap_or_else ( |_| "events" . into ( ) ) ;
568- let topic_clean = env:: var ( "IGGY_TOPIC_CLEAN" ) . unwrap_or_else ( |_| "events-clean" . into ( ) ) ;
568+ let topic_name = env:: var ( "IGGY_TOPIC" ) . unwrap_or_else ( |_| "events" . into ( ) ) ;
569+ let topic_clean_name = env:: var ( "IGGY_TOPIC_CLEAN" ) . unwrap_or_else ( |_| "events-clean" . into ( ) ) ;
569570
570571 let http = reqwest:: Client :: new ( ) ;
571572
@@ -575,45 +576,77 @@ async fn debug_iggy_handler() -> Json<serde_json::Value> {
575576 Err ( e) => return Json ( json ! ( { "error" : format!( "login failed: {}" , e) , "iggy_http_url" : iggy_http_url } ) ) ,
576577 } ;
577578
578- // Poll partition 0, offset 0, count 1 from events topic
579- let events_url = format ! (
580- "{}/streams/{}/topics/{}/messages?consumer_id=debug&partition_id=0&polling_strategy=offset&value=0&count=1" ,
581- iggy_http_url, stream, topic
582- ) ;
583- let events_resp = http. get ( & events_url)
584- . header ( "Authorization" , format ! ( "Bearer {}" , token) )
585- . send ( ) . await ;
586- let events_raw = match events_resp {
587- Ok ( r) => {
588- let status = r. status ( ) . as_u16 ( ) ;
589- let body = r. text ( ) . await . unwrap_or_else ( |e| format ! ( "read error: {}" , e) ) ;
590- json ! ( { "status" : status, "body_preview" : & body[ ..body. len( ) . min( 500 ) ] } )
579+ // Helper: get topic info (partition count + stats)
580+ let topic_info = |topic : & str | {
581+ let url = format ! ( "{}/streams/{}/topics/{}" , iggy_http_url, stream, topic) ;
582+ let http = http. clone ( ) ;
583+ let token = token. clone ( ) ;
584+ async move {
585+ match http. get ( & url) . header ( "Authorization" , format ! ( "Bearer {}" , token) ) . send ( ) . await {
586+ Ok ( r) => {
587+ let status = r. status ( ) . as_u16 ( ) ;
588+ let body = r. text ( ) . await . unwrap_or_default ( ) ;
589+ json ! ( { "status" : status, "body" : serde_json:: from_str:: <serde_json:: Value >( & body) . unwrap_or( json!( body) ) } )
590+ }
591+ Err ( e) => json ! ( { "error" : format!( "{}" , e) } ) ,
592+ }
591593 }
592- Err ( e) => json ! ( { "error" : format!( "{}" , e) } ) ,
593594 } ;
594595
595- // Poll partition 0, offset 0, count 1 from events-clean topic
596- let clean_url = format ! (
597- "{}/streams/{}/topics/{}/messages?consumer_id=debug&partition_id=0&polling_strategy=offset&value=0&count=1" ,
598- iggy_http_url, stream, topic_clean
599- ) ;
600- let clean_resp = http. get ( & clean_url)
601- . header ( "Authorization" , format ! ( "Bearer {}" , token) )
602- . send ( ) . await ;
603- let clean_raw = match clean_resp {
604- Ok ( r) => {
605- let status = r. status ( ) . as_u16 ( ) ;
606- let body = r. text ( ) . await . unwrap_or_else ( |e| format ! ( "read error: {}" , e) ) ;
607- json ! ( { "status" : status, "body_preview" : & body[ ..body. len( ) . min( 500 ) ] } )
596+ // Helper: scan all partitions for a topic, report which ones have data
597+ let scan_partitions = |topic : String , partitions : u32 | {
598+ let http = http. clone ( ) ;
599+ let token = token. clone ( ) ;
600+ let iggy_http_url = iggy_http_url. clone ( ) ;
601+ let stream = stream. clone ( ) ;
602+ async move {
603+ let mut results = Vec :: new ( ) ;
604+ for pid in 0 ..partitions {
605+ let url = format ! (
606+ "{}/streams/{}/topics/{}/messages?consumer_id=debug&partition_id={}&polling_strategy=offset&value=0&count=1" ,
607+ iggy_http_url, stream, topic, pid
608+ ) ;
609+ match http. get ( & url) . header ( "Authorization" , format ! ( "Bearer {}" , token) ) . send ( ) . await {
610+ Ok ( r) => {
611+ let body: serde_json:: Value = r. json ( ) . await . unwrap_or ( json ! ( { } ) ) ;
612+ let count = body. get ( "count" ) . and_then ( |c| c. as_u64 ( ) ) . unwrap_or ( 0 ) ;
613+ let current_offset = body. get ( "current_offset" ) . and_then ( |c| c. as_u64 ( ) ) . unwrap_or ( 0 ) ;
614+ if count > 0 || current_offset > 0 {
615+ results. push ( json ! ( { "partition" : pid, "count" : count, "current_offset" : current_offset } ) ) ;
616+ }
617+ }
618+ Err ( _) => { }
619+ }
620+ }
621+ results
608622 }
609- Err ( e) => json ! ( { "error" : format!( "{}" , e) } ) ,
610623 } ;
611624
625+ let events_info = topic_info ( & topic_name) . await ;
626+ let clean_info = topic_info ( & topic_clean_name) . await ;
627+
628+ // Extract partition counts
629+ let events_partitions = events_info. pointer ( "/body/partitions_count" )
630+ . and_then ( |v| v. as_u64 ( ) ) . unwrap_or ( 24 ) as u32 ;
631+ let clean_partitions = clean_info. pointer ( "/body/partitions_count" )
632+ . and_then ( |v| v. as_u64 ( ) ) . unwrap_or ( 24 ) as u32 ;
633+
634+ let events_data = scan_partitions ( topic_name. clone ( ) , events_partitions) . await ;
635+ let clean_data = scan_partitions ( topic_clean_name. clone ( ) , clean_partitions) . await ;
636+
612637 Json ( json ! ( {
613638 "iggy_http_url" : iggy_http_url,
614639 "login" : "ok" ,
615- "events_topic" : { "url" : events_url, "response" : events_raw } ,
616- "events_clean_topic" : { "url" : clean_url, "response" : clean_raw } ,
640+ "events_topic" : {
641+ "name" : topic_name,
642+ "info" : events_info,
643+ "partitions_with_data" : events_data,
644+ } ,
645+ "events_clean_topic" : {
646+ "name" : topic_clean_name,
647+ "info" : clean_info,
648+ "partitions_with_data" : clean_data,
649+ } ,
617650 } ) )
618651}
619652
0 commit comments