fix(stats): try to fix the stats extraction, again

This commit is contained in:
perf3ct 2025-07-08 21:18:21 +00:00
parent e628b0d4d5
commit a6f2b6df09
2 changed files with 180 additions and 7 deletions

View File

@ -6,6 +6,7 @@ use std::sync::Arc;
use tower_http::{cors::CorsLayer, services::{ServeDir, ServeFile}};
use tracing::{info, error, warn};
use anyhow;
use sqlx::{Row, Column};
use readur::{config::Config, db::Database, AppState, *};
@ -224,6 +225,12 @@ async fn main() -> anyhow::Result<()> {
match function_check {
Ok(Some(def)) => {
info!("📋 get_ocr_queue_stats function definition retrieved");
// Debug: print the actual function definition
info!("🔍 Function definition (first 500 chars): {}",
def.chars().take(500).collect::<String>());
// Check if it contains the correct logic from our latest migration
if def.contains("document_stats") && def.contains("ocr_status = 'completed'") {
info!("✅ get_ocr_queue_stats function has correct logic (uses documents table for completed_today)");
@ -231,6 +238,22 @@ async fn main() -> anyhow::Result<()> {
error!("❌ get_ocr_queue_stats function still uses old logic - migration may have failed");
info!("Function uses ocr_queue table instead of documents table for completed_today count");
}
// Test the function execution at startup
info!("🧪 Testing function execution at startup...");
match sqlx::query("SELECT * FROM get_ocr_queue_stats()").fetch_one(web_db.get_pool()).await {
Ok(test_result) => {
info!("✅ Function executes successfully at startup");
let columns = test_result.columns();
info!("🔍 Function returns {} columns at startup:", columns.len());
for (i, column) in columns.iter().enumerate() {
info!(" Column {}: name='{}', type='{:?}'", i, column.name(), column.type_info());
}
}
Err(e) => {
error!("❌ Function fails to execute at startup: {}", e);
}
}
}
Ok(None) => error!("❌ get_ocr_queue_stats function does not exist after migration"),
Err(e) => error!("❌ Failed to verify get_ocr_queue_stats function: {}", e),

View File

@ -1,7 +1,7 @@
use anyhow::Result;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, PgPool, Row};
use sqlx::{FromRow, PgPool, Row, Column};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use tokio::sync::Semaphore;
@ -793,6 +793,39 @@ impl OcrQueueService {
/// Get queue statistics
pub async fn get_stats(&self) -> Result<QueueStats> {
tracing::debug!("OCR Queue: Starting get_stats() call");
// First, let's check the function signature/return type
let function_info = sqlx::query(
r#"
SELECT
p.proname as function_name,
pg_get_function_result(p.oid) as return_type,
pg_get_function_arguments(p.oid) as arguments
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = 'public' AND p.proname = 'get_ocr_queue_stats'
"#
)
.fetch_optional(&self.pool)
.await
.map_err(|e| {
tracing::error!("Failed to get function info: {}", e);
e
})?;
if let Some(info) = function_info {
let function_name: String = info.get("function_name");
let return_type: String = info.get("return_type");
let arguments: String = info.get("arguments");
tracing::debug!("Function info - name: {}, return_type: {}, arguments: {}", function_name, return_type, arguments);
} else {
tracing::error!("get_ocr_queue_stats function not found!");
return Err(anyhow::anyhow!("get_ocr_queue_stats function not found"));
}
tracing::debug!("OCR Queue: Calling get_ocr_queue_stats() function");
let stats = sqlx::query(
r#"
SELECT * FROM get_ocr_queue_stats()
@ -802,16 +835,133 @@ impl OcrQueueService {
.await
.map_err(|e| {
tracing::error!("Failed to get OCR queue stats: {}", e);
tracing::debug!("This indicates a function structure mismatch error");
e
})?;
tracing::debug!("OCR Queue: Successfully got result from function, analyzing structure...");
// Debug the actual columns returned
let columns = stats.columns();
tracing::debug!("Function returned {} columns:", columns.len());
for (i, column) in columns.iter().enumerate() {
let column_name = column.name();
let column_type = column.type_info();
tracing::debug!(" Column {}: name='{}', type='{:?}'", i, column_name, column_type);
}
// Try to extract values with detailed logging
tracing::debug!("Attempting to extract pending_count...");
let pending_count = match stats.try_get::<i64, _>("pending_count") {
Ok(val) => {
tracing::debug!("Successfully got pending_count: {}", val);
val
}
Err(e) => {
tracing::error!("Failed to get pending_count: {}", e);
tracing::debug!("Trying different type for pending_count...");
stats.try_get::<Option<i64>, _>("pending_count")
.map_err(|e2| {
tracing::error!("Also failed with Option<i64>: {}", e2);
e
})?
.unwrap_or(0)
}
};
tracing::debug!("Attempting to extract processing_count...");
let processing_count = match stats.try_get::<i64, _>("processing_count") {
Ok(val) => {
tracing::debug!("Successfully got processing_count: {}", val);
val
}
Err(e) => {
tracing::error!("Failed to get processing_count: {}", e);
stats.try_get::<Option<i64>, _>("processing_count")?.unwrap_or(0)
}
};
tracing::debug!("Attempting to extract failed_count...");
let failed_count = match stats.try_get::<i64, _>("failed_count") {
Ok(val) => {
tracing::debug!("Successfully got failed_count: {}", val);
val
}
Err(e) => {
tracing::error!("Failed to get failed_count: {}", e);
stats.try_get::<Option<i64>, _>("failed_count")?.unwrap_or(0)
}
};
tracing::debug!("Attempting to extract completed_today...");
let completed_today = match stats.try_get::<i64, _>("completed_today") {
Ok(val) => {
tracing::debug!("Successfully got completed_today: {}", val);
val
}
Err(e) => {
tracing::error!("Failed to get completed_today: {}", e);
stats.try_get::<Option<i64>, _>("completed_today")?.unwrap_or(0)
}
};
tracing::debug!("Attempting to extract avg_wait_time_minutes...");
let avg_wait_time_minutes = match stats.try_get::<Option<f64>, _>("avg_wait_time_minutes") {
Ok(val) => {
tracing::debug!("Successfully got avg_wait_time_minutes: {:?}", val);
val
}
Err(e) => {
tracing::error!("Failed to get avg_wait_time_minutes: {}", e);
// Try as string and convert
match stats.try_get::<Option<String>, _>("avg_wait_time_minutes") {
Ok(Some(str_val)) => {
let float_val = str_val.parse::<f64>().ok();
tracing::debug!("Converted string '{}' to f64: {:?}", str_val, float_val);
float_val
}
Ok(None) => None,
Err(e2) => {
tracing::error!("Also failed with String: {}", e2);
return Err(anyhow::anyhow!("Failed to get avg_wait_time_minutes: {}", e));
}
}
}
};
tracing::debug!("Attempting to extract oldest_pending_minutes...");
let oldest_pending_minutes = match stats.try_get::<Option<f64>, _>("oldest_pending_minutes") {
Ok(val) => {
tracing::debug!("Successfully got oldest_pending_minutes: {:?}", val);
val
}
Err(e) => {
tracing::error!("Failed to get oldest_pending_minutes: {}", e);
// Try as string and convert
match stats.try_get::<Option<String>, _>("oldest_pending_minutes") {
Ok(Some(str_val)) => {
let float_val = str_val.parse::<f64>().ok();
tracing::debug!("Converted string '{}' to f64: {:?}", str_val, float_val);
float_val
}
Ok(None) => None,
Err(e2) => {
tracing::error!("Also failed with String: {}", e2);
return Err(anyhow::anyhow!("Failed to get oldest_pending_minutes: {}", e));
}
}
}
};
tracing::debug!("OCR Queue: Successfully extracted all values, creating QueueStats");
Ok(QueueStats {
pending_count: stats.get("pending_count"),
processing_count: stats.get("processing_count"),
failed_count: stats.get("failed_count"),
completed_today: stats.get("completed_today"),
avg_wait_time_minutes: stats.get("avg_wait_time_minutes"),
oldest_pending_minutes: stats.get("oldest_pending_minutes"),
pending_count,
processing_count,
failed_count,
completed_today,
avg_wait_time_minutes,
oldest_pending_minutes,
})
}