Ir al contenido principal
IngenieríaMar 28, 2026

Deep EVM #24: Propagación de Contexto en Rust Asíncrono — Deadlines, Cancelación y Tracing

OS
Open Soft Team

Engineering Team

El problema del contexto en async Rust

En Go, el paquete context propaga deadlines, cancelación y valores a través de la cadena de llamadas. Rust no tiene un equivalente estándar, pero podemos construir patrones similares con tokio.

Deadlines con tokio::time::timeout

use tokio::time::{timeout, Duration};

async fn process_block(block: Block) -> Result<Vec<Bundle>> {
    // Deadline total: 10 segundos
    let result = timeout(
        Duration::from_secs(10),
        process_block_inner(block)
    ).await;
    
    match result {
        Ok(Ok(bundles)) => Ok(bundles),
        Ok(Err(e)) => Err(e),
        Err(_) => {
            tracing::warn!("Block processing timed out");
            Ok(vec![]) // Retornar vacío en timeout
        }
    }
}

Cancelación cooperativa con CancellationToken

use tokio_util::sync::CancellationToken;

async fn worker(token: CancellationToken) {
    loop {
        tokio::select! {
            _ = token.cancelled() => {
                tracing::info!("Worker cancelado");
                return;
            }
            result = do_work() => {
                handle_result(result);
            }
        }
    }
}

// Uso:
let token = CancellationToken::new();
let child_token = token.child_token();

tokio::spawn(worker(child_token));

// Cancelar todos los workers:
token.cancel();

Propagación de trace IDs

Para correlacionar logs a través de componentes async:

use tracing::{instrument, Span};
use uuid::Uuid;

#[instrument(fields(request_id = %Uuid::new_v4()))]
async fn handle_request(req: Request) -> Response {
    let result = process(req).await; // Hereda el span
    respond(result).await            // También hereda
}

Struct de contexto personalizado

struct PipelineContext {
    deadline: Instant,
    cancellation: CancellationToken,
    trace_id: String,
    block_number: u64,
}

impl PipelineContext {
    fn remaining(&self) -> Duration {
        self.deadline.saturating_duration_since(Instant::now())
    }
    
    fn is_expired(&self) -> bool {
        Instant::now() >= self.deadline
    }
}

Este contexto se pasa a cada etapa del pipeline, permitiendo cancelación temprana y seguimiento distribuido.

Conclusión

La propagación de contexto en Rust asíncrono requiere composición manual de timeout, CancellationToken y tracing spans. Aunque no es tan integrado como el context.Context de Go, los patrones resultantes son más explícitos y tipo-seguros.

Etiquetas