use super::super::args::{EnrichArgs, EnrichOperation};
use super::super::queue::{
count_eligible_pending, enqueue_candidate, item_type_for_key,
reconcile_satisfied_reembed_pending, reopen_force_redescribe_candidates, reset_failed_for_op,
reset_processing_for_op, reset_stale_processing_claims,
};
use crate::errors::AppError;
use rusqlite::Connection;
pub(super) fn prepare_queue(
queue_conn: &Connection,
conn: &Connection,
args: &EnrichArgs,
namespace: &str,
op_label: &str,
scan_keys: &[String],
) -> Result<(), AppError> {
{
let stale_reset = reset_stale_processing_claims(queue_conn, args.stale_claim_secs)?;
if stale_reset > 0 {
tracing::info!(
target: "enrich",
count = stale_reset,
max_age_secs = args.stale_claim_secs,
"reset stale processing claims (older than threshold)"
);
}
}
if args.resume {
let reset = reset_processing_for_op(queue_conn, op_label, namespace)
.map_err(|e| AppError::Validation(crate::i18n::validation::queue_resume_failed(&e)))?;
if reset > 0 {
tracing::info!(target: "enrich", count = reset, "reset stuck processing items to pending");
}
}
if args.retry_failed {
let count = reset_failed_for_op(queue_conn, op_label, namespace).map_err(|e| {
AppError::Validation(crate::i18n::validation::queue_retry_failed_reset_failed(&e))
})?;
tracing::info!(target: "enrich", count, "retrying failed items");
}
if !args.resume && !args.retry_failed && !args.until_empty {
queue_conn
.execute(
"DELETE FROM queue WHERE operation = ?1 \
AND (namespace = ?2 OR namespace = '' OR namespace IS NULL)",
rusqlite::params![op_label, namespace],
)
.map_err(|e| AppError::Validation(crate::i18n::validation::queue_clear_failed(&e)))?;
}
if args.force_redescribe && matches!(args.operation(), EnrichOperation::EntityDescriptions) {
let reopened = reopen_force_redescribe_candidates(queue_conn, namespace, scan_keys);
if reopened > 0 {
tracing::info!(
target: "enrich",
reopened,
scan = scan_keys.len(),
"force-redescribe reopened skipped/done candidates (once per run)"
);
}
}
if matches!(args.operation(), EnrichOperation::ReEmbed) {
match reconcile_satisfied_reembed_pending(conn, queue_conn, namespace) {
Ok(n) if n > 0 => {
tracing::info!(
target: "enrich",
reconciled = n,
"re-embed reconciled pending rows with live embeddings"
);
}
Ok(_) => {}
Err(e) => {
tracing::warn!(target: "enrich", error = %e, "re-embed reconcile failed");
}
}
}
Ok(())
}
pub(super) fn enqueue_batch(
queue_conn: &mut Connection,
conn: &Connection,
namespace: &str,
keys: &[String],
item_type: &'static str,
op_label: &str,
) -> Result<(), AppError> {
let tx = queue_conn.transaction()?;
let tx_conn: &Connection = &tx;
for key in keys.iter() {
let it = item_type_for_key(key, item_type);
enqueue_candidate(tx_conn, conn, namespace, key, it, op_label);
}
tx.commit()?;
Ok(())
}
pub(super) fn log_enqueue_result(
queue_conn: &Connection,
op_label: &str,
namespace: &str,
scan_len: usize,
) {
let pending_now = count_eligible_pending(queue_conn, op_label, namespace, "");
tracing::info!(
target: "enrich",
scan = scan_len,
pending_after_enqueue = pending_now,
"enqueue complete"
);
}
pub(super) fn enqueue_page(
queue_conn: &mut Connection,
conn: &Connection,
namespace: &str,
keys: &[String],
item_type: &'static str,
op_label: &str,
) -> Result<(), AppError> {
if keys.is_empty() {
return Ok(());
}
enqueue_batch(queue_conn, conn, namespace, keys, item_type, op_label)
}
pub(super) fn reopen_force_redescribe_page(
queue_conn: &Connection,
namespace: &str,
keys: &[String],
) -> usize {
if keys.is_empty() {
return 0;
}
reopen_force_redescribe_candidates(queue_conn, namespace, keys)
}