انتقل إلى المحتوى الرئيسي
IngenieurwesenMar 28, 2026

Deep EVM #24: Kontextpropagierung in async Rust — Deadlines, Abbruch und Tracing

OS
Open Soft Team

Engineering Team

Das fehlende Stueck: context.Context fuer Rust

Go hat context.Context fuer das Weiterreichen von Deadlines, Abbruchsignalen und Metadaten ueber API-Grenzen hinweg. Rust hat kein eingebautes Aequivalent. Dieser Artikel baut einen einheitlichen Kontexttyp fuer async Rust mit Deadline-bewussten Operationen, hierarchischem Abbruch und Tracing-Span-Propagierung.

CancellationToken

tokio_util bietet CancellationToken fuer kooperativen Abbruch:

use tokio_util::sync::CancellationToken;

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

// Aufgabe mit Abbruchunterstuetzung
tokio::spawn(async move {
    tokio::select! {
        result = do_work() => {
            println!("Arbeit abgeschlossen: {:?}", result);
        }
        _ = child_token.cancelled() => {
            println!("Abgebrochen!");
        }
    }
});

// Spaeter: Alles abbrechen
token.cancel();

Deadline-bewusste Operationen

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

struct Context {
    cancel: CancellationToken,
    deadline: Option<Instant>,
    span: tracing::Span,
}

impl Context {
    fn with_timeout(parent: &Context, duration: Duration) -> Self {
        let deadline = Instant::now() + duration;
        Self {
            cancel: parent.cancel.child_token(),
            deadline: Some(deadline),
            span: parent.span.clone(),
        }
    }
    
    async fn run<F, T>(&self, future: F) -> Result<T, ContextError>
    where
        F: Future<Output = T>,
    {
        let _guard = self.span.enter();
        
        tokio::select! {
            result = future => Ok(result),
            _ = self.cancel.cancelled() => Err(ContextError::Cancelled),
            _ = self.sleep_until_deadline() => Err(ContextError::DeadlineExceeded),
        }
    }
}

Tracing-Span-Propagierung

use tracing::{instrument, Span};

#[instrument(parent = &ctx.span, skip(ctx, db))]
async fn process_block(
    ctx: &Context,
    db: &Database,
    block: &Block,
) -> Result<(), Error> {
    let span = tracing::info_span!("process_transactions",
        count = block.transactions.len());
    
    for tx in &block.transactions {
        let tx_ctx = Context::with_timeout(ctx, Duration::from_secs(5));
        tx_ctx.run(process_transaction(db, tx)).await??;
    }
    
    Ok(())
}

Hierarchischer Abbruch

Das Schoene an CancellationToken: Kind-Token werden automatisch abgebrochen, wenn der Eltern-Token abgebrochen wird:

Root Token
  |- Block-Verarbeitung Token
  |    |- Transaktion 1 Token
  |    |- Transaktion 2 Token
  |- API-Server Token
       |- Request 1 Token
       |- Request 2 Token

Wenn der Root-Token abgebrochen wird (z.B. SIGTERM), werden alle Kind-Aufgaben sauber heruntergefahren.

Fazit

Kontextpropagierung in async Rust erfordert etwas mehr Aufwand als in Go, aber CancellationToken, tokio::select! und Tracing-Spans bieten alle Bausteine. Der Schluessel: Definieren Sie einen einheitlichen Context-Typ und verwenden Sie ihn konsistent durch Ihre gesamte Anwendung.

الوسوم