Retrieving Results
Retrieving Results
Section titled “Retrieving Results”Retrieve task outcomes through TaskHandle::get(). For error handling
strategy, see Error Handling and Result
Handling.
How To
Section titled “How To”Basic Retrieval
Section titled “Basic Retrieval”use std::time::Duration;use horsies::TaskResult;
let handle = match my_task::send(input).await { Ok(h) => h, Err(err) => { println!("Send failed: {}", err.message); return; }};
// Block until complete (or timeout/error)let result: TaskResult<MyOutput> = handle.get(None).await;
match result { TaskResult::Ok(value) => println!("Success: {:?}", value), TaskResult::Err(err) => { if err.is_transient() { println!("Transient error, task may complete later"); } else { println!("Error: {:?} - {:?}", err.error_code, err.message); } }}Timeouts
Section titled “Timeouts”Specify maximum wait time as a Duration:
use std::time::Duration;
// Wait up to 5 secondslet result = handle.get(Some(Duration::from_secs(5))).await;
if result.is_err() { let err = result.unwrap_err(); // Check if it was a timeout (task may still be running) if err.is_transient() { println!("Timed out, task may still complete later"); }}Result Caching
Section titled “Result Caching”Results are cached on the handle after the first terminal resolution:
let handle = match my_task::send(input).await { Ok(h) => h, Err(err) => { println!("Send failed: {}", err.message); return; }};
// First call fetches from databaselet result1 = handle.get(None).await;
// Subsequent calls return cached resultlet result2 = handle.get(None).await; // No database queryChecking Result State
Section titled “Checking Result State”let result = handle.get(None).await;
// Check stateif result.is_ok() { let value = result.unwrap();} else { let error = result.unwrap_err();}
// Or use pattern matchingmatch result { TaskResult::Ok(value) => { /* use value */ } TaskResult::Err(err) => { /* handle error */ }}Task Metadata and Attempt History
Section titled “Task Metadata and Attempt History”Use handle.info() to fetch task metadata without waiting for completion:
let info_result = handle.info(true, true, false).await;if let Ok(Some(task_info)) = info_result { println!("Status: {:?}, Retries: {}", task_info.status, task_info.retry_count); println!("Error code: {:?}", task_info.error_code);}To inspect per-attempt execution history, pass include_attempts = true:
let info_result = handle.info(false, false, true).await;if let Ok(Some(task_info)) = info_result { if let Some(attempts) = task_info.attempts { for attempt in &attempts { println!( "Attempt {}: {:?} (retry={}, error={:?})", attempt.attempt, attempt.outcome, attempt.will_retry, attempt.error_code ); } }}info() reads both live and history storage. A terminal task’s attempt list is
decoded from its history snapshot. Corrupt result or attempt envelopes return
an error. They are not guessed.
Rerun a retained terminal task
Section titled “Rerun a retained terminal task”rerun_task creates a new task from retained source input. It never changes
the source record. The new task stores its direct source and its rerun root.
use horsies::{ rerun_task, RerunEnqueuePolicy, RerunOutcome, RerunTask,};
let source_id = handle.task_id();let broker = app.get_broker().await?;
let outcome = rerun_task( &broker, RerunTask::new( source_id, None, Some("manual-rerun-4182".to_owned()), ), RerunEnqueuePolicy::standard(true),).await?;
match outcome { RerunOutcome::Enqueued { new_task_id, .. } => { println!("rerun task: {new_task_id}"); } RerunOutcome::KeyReplay { existing_task_id } => { println!("existing rerun: {existing_task_id}"); } RerunOutcome::SourceLive { task_id } => { println!("source is live: {task_id}"); } RerunOutcome::SourceAbsent { task_id, .. } => { println!("source is absent: {task_id}"); } RerunOutcome::NotEligible { task_id, reason } => { println!("source is not eligible: {task_id} {reason:?}"); } RerunOutcome::InputUnavailable { task_id, .. } => { println!("source input is unavailable: {task_id}"); } RerunOutcome::InputCorrupt { task_id, detail } => { println!("source input is corrupt: {task_id} {detail}"); } RerunOutcome::KeyConflict { task_id, .. } => { println!("rerun key conflicts: {task_id}"); }}The source must be a retained terminal non-workflow task. A completed source is not eligible. A workflow backing task is not eligible.
The source input must have an INLINE rerun envelope. Set
PostgresConfig.retain_rerun_input_default = true before enqueue when later
rerun is required. The default is false.
The policy selects the new retention class. It also selects whether the new
task retains its own rerun input. RerunEnqueuePolicy::standard(true) uses
standard_30d and a 24-hour key reservation window.
RerunOutcome is exhaustive:
EnqueuedSourceLiveSourceAbsentNotEligibleInputUnavailableInputCorruptKeyConflictKeyReplay
Use a caller key when an operator or API request may be submitted more than once. The rerun lineage is part of the command fingerprint. A source enqueue key cannot silently replay a rerun command.
Things to Avoid
Section titled “Things to Avoid”Don’t silently ignore errors by only logging them.
// Wrong - logs error but takes no actionlet result = handle.get(None).await;if result.is_err() { println!("{:?}", result.unwrap_err().message);}// Code continues as if nothing happened...
// Correct - handle or propagate errorslet result = handle.get(None).await;match result { TaskResult::Err(err) => { handle_error(&err); return Err(err.into()); } TaskResult::Ok(value) => { println!("Success: {:?}", value); }}For detailed error handling patterns, see Error Handling.