[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-24-propagation-contexte-async-rust":3},{"article":4,"author":51},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":16,"meta_description":17,"focus_keyword":18,"og_image":19,"canonical_url":19,"robots_meta":20,"created_at":15,"updated_at":15,"tags":21,"category_name":31,"related_articles":32},"d6000000-0000-0000-0000-000000000124","a0000000-0000-0000-0000-000000000066","Deep EVM #24 : Propagation de contexte en Rust async — Délais, annulation et traçage","deep-evm-24-propagation-contexte-async-rust","Implémentez la propagation de contexte en Rust async : délais avec tokio::time::timeout, annulation coopérative avec CancellationToken, et traçage distribué avec tracing et OpenTelemetry.","## Le problème du contexte\n\nDans les systèmes async, les opérations traversent de nombreuses tâches et services. Comment propager les délais, les identifiants de requête et les signaux d'annulation à travers cette chaîne ?\n\nEn Go, le package `context.Context` résout ce problème de manière standard. En Rust, il n'y a pas d'équivalent unique — vous devez combiner plusieurs outils.\n\n## Délais avec tokio::time::timeout\n\n```rust\nuse tokio::time::{timeout, Duration};\n\nasync fn process_with_deadline(data: &Data) -> Result\u003COutput> {\n    let result = timeout(\n        Duration::from_secs(5),\n        heavy_computation(data)\n    ).await;\n\n    match result {\n        Ok(Ok(output)) => Ok(output),\n        Ok(Err(e)) => Err(e),\n        Err(_) => Err(Error::Timeout),\n    }\n}\n```\n\n### Délais imbriqués\n\nPour propager un délai à travers les sous-opérations :\n\n```rust\nasync fn pipeline(deadline: Instant) -> Result\u003C()> {\n    let remaining = deadline - Instant::now();\n\n    \u002F\u002F Chaque étape utilise le temps restant\n    timeout(remaining, step_one()).await??;\n\n    let remaining = deadline - Instant::now();\n    timeout(remaining, step_two()).await??;\n\n    Ok(())\n}\n```\n\n## Annulation coopérative avec CancellationToken\n\n```rust\nuse tokio_util::sync::CancellationToken;\n\nlet token = CancellationToken::new();\nlet child_token = token.child_token();\n\n\u002F\u002F Tâche de travail\ntokio::spawn(async move {\n    loop {\n        tokio::select! {\n            _ = child_token.cancelled() => {\n                tracing::info!(\"Tâche annulée proprement\");\n                break;\n            }\n            result = do_work() => {\n                handle(result);\n            }\n        }\n    }\n});\n\n\u002F\u002F Annuler depuis le parent\ntoken.cancel();\n```\n\nLe CancellationToken supporte les hiérarchies — annuler un parent annule tous les enfants.\n\n## Traçage distribué\n\n```rust\nuse tracing::{instrument, info_span, Instrument};\n\n#[instrument(skip(db), fields(user_id = %user_id))]\nasync fn get_user(db: &Database, user_id: UserId) -> Result\u003CUser> {\n    let user = db.query_user(user_id)\n        .instrument(info_span!(\"db_query\"))\n        .await?;\n\n    let enriched = enrich_user(user)\n        .instrument(info_span!(\"enrich\"))\n        .await?;\n\n    Ok(enriched)\n}\n```\n\n### OpenTelemetry\n\nPour le traçage inter-services :\n\n```rust\nuse tracing_opentelemetry::OpenTelemetryLayer;\nuse opentelemetry::sdk::trace::TracerProvider;\n\nfn init_tracing() {\n    let provider = TracerProvider::builder()\n        .with_exporter(opentelemetry_jaeger::new_agent_pipeline())\n        .build();\n\n    let telemetry = OpenTelemetryLayer::new(provider.tracer(\"my-service\"));\n\n    tracing_subscriber::registry()\n        .with(telemetry)\n        .with(tracing_subscriber::fmt::layer())\n        .init();\n}\n```\n\n## Pattern complet : contexte de requête\n\nCombinez le tout dans un contexte de requête :\n\n```rust\nstruct RequestContext {\n    request_id: Uuid,\n    deadline: Instant,\n    cancellation: CancellationToken,\n    span: tracing::Span,\n}\n\nimpl RequestContext {\n    fn remaining_time(&self) -> Duration {\n        self.deadline.saturating_duration_since(Instant::now())\n    }\n\n    fn child(&self, name: &str) -> Self {\n        Self {\n            request_id: self.request_id,\n            deadline: self.deadline,\n            cancellation: self.cancellation.child_token(),\n            span: info_span!(parent: &self.span, \"child\", name = name),\n        }\n    }\n}\n```\n\n## Conclusion\n\nLa propagation de contexte en Rust async combine timeout pour les délais, CancellationToken pour l'annulation coopérative et tracing pour l'observabilité. Ensemble, ils fournissent la fonctionnalité équivalente au context.Context de Go, avec la sécurité de type de Rust.","\u003Ch2 id=\"le-probl-me-du-contexte\">Le problème du contexte\u003C\u002Fh2>\n\u003Cp>Dans les systèmes async, les opérations traversent de nombreuses tâches et services. Comment propager les délais, les identifiants de requête et les signaux d’annulation à travers cette chaîne ?\u003C\u002Fp>\n\u003Cp>En Go, le package \u003Ccode>context.Context\u003C\u002Fcode> résout ce problème de manière standard. En Rust, il n’y a pas d’équivalent unique — vous devez combiner plusieurs outils.\u003C\u002Fp>\n\u003Ch2 id=\"d-lais-avec-tokio-time-timeout\">Délais avec tokio::time::timeout\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">use tokio::time::{timeout, Duration};\n\nasync fn process_with_deadline(data: &amp;Data) -&gt; Result&lt;Output&gt; {\n    let result = timeout(\n        Duration::from_secs(5),\n        heavy_computation(data)\n    ).await;\n\n    match result {\n        Ok(Ok(output)) =&gt; Ok(output),\n        Ok(Err(e)) =&gt; Err(e),\n        Err(_) =&gt; Err(Error::Timeout),\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Délais imbriqués\u003C\u002Fh3>\n\u003Cp>Pour propager un délai à travers les sous-opérations :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">async fn pipeline(deadline: Instant) -&gt; Result&lt;()&gt; {\n    let remaining = deadline - Instant::now();\n\n    \u002F\u002F Chaque étape utilise le temps restant\n    timeout(remaining, step_one()).await??;\n\n    let remaining = deadline - Instant::now();\n    timeout(remaining, step_two()).await??;\n\n    Ok(())\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"annulation-coop-rative-avec-cancellationtoken\">Annulation coopérative avec CancellationToken\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">use tokio_util::sync::CancellationToken;\n\nlet token = CancellationToken::new();\nlet child_token = token.child_token();\n\n\u002F\u002F Tâche de travail\ntokio::spawn(async move {\n    loop {\n        tokio::select! {\n            _ = child_token.cancelled() =&gt; {\n                tracing::info!(\"Tâche annulée proprement\");\n                break;\n            }\n            result = do_work() =&gt; {\n                handle(result);\n            }\n        }\n    }\n});\n\n\u002F\u002F Annuler depuis le parent\ntoken.cancel();\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Le CancellationToken supporte les hiérarchies — annuler un parent annule tous les enfants.\u003C\u002Fp>\n\u003Ch2 id=\"tra-age-distribu\">Traçage distribué\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">use tracing::{instrument, info_span, Instrument};\n\n#[instrument(skip(db), fields(user_id = %user_id))]\nasync fn get_user(db: &amp;Database, user_id: UserId) -&gt; Result&lt;User&gt; {\n    let user = db.query_user(user_id)\n        .instrument(info_span!(\"db_query\"))\n        .await?;\n\n    let enriched = enrich_user(user)\n        .instrument(info_span!(\"enrich\"))\n        .await?;\n\n    Ok(enriched)\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>OpenTelemetry\u003C\u002Fh3>\n\u003Cp>Pour le traçage inter-services :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">use tracing_opentelemetry::OpenTelemetryLayer;\nuse opentelemetry::sdk::trace::TracerProvider;\n\nfn init_tracing() {\n    let provider = TracerProvider::builder()\n        .with_exporter(opentelemetry_jaeger::new_agent_pipeline())\n        .build();\n\n    let telemetry = OpenTelemetryLayer::new(provider.tracer(\"my-service\"));\n\n    tracing_subscriber::registry()\n        .with(telemetry)\n        .with(tracing_subscriber::fmt::layer())\n        .init();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"pattern-complet-contexte-de-requ-te\">Pattern complet : contexte de requête\u003C\u002Fh2>\n\u003Cp>Combinez le tout dans un contexte de requête :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">struct RequestContext {\n    request_id: Uuid,\n    deadline: Instant,\n    cancellation: CancellationToken,\n    span: tracing::Span,\n}\n\nimpl RequestContext {\n    fn remaining_time(&amp;self) -&gt; Duration {\n        self.deadline.saturating_duration_since(Instant::now())\n    }\n\n    fn child(&amp;self, name: &amp;str) -&gt; Self {\n        Self {\n            request_id: self.request_id,\n            deadline: self.deadline,\n            cancellation: self.cancellation.child_token(),\n            span: info_span!(parent: &amp;self.span, \"child\", name = name),\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"conclusion\">Conclusion\u003C\u002Fh2>\n\u003Cp>La propagation de contexte en Rust async combine timeout pour les délais, CancellationToken pour l’annulation coopérative et tracing pour l’observabilité. Ensemble, ils fournissent la fonctionnalité équivalente au context.Context de Go, avec la sécurité de type de Rust.\u003C\u002Fp>\n","fr","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:29.333799Z","Propagation de contexte en Rust async — Délais, annulation et traçage","Propagation de contexte en Rust async : timeout tokio, CancellationToken, traçage distribué avec tracing et OpenTelemetry.","propagation contexte rust async",null,"index, follow",[22,27],{"id":23,"name":24,"slug":25,"created_at":26},"c0000000-0000-0000-0000-000000000022","Performance","performance","2026-03-28T10:44:21.513630Z",{"id":28,"name":29,"slug":30,"created_at":26},"c0000000-0000-0000-0000-000000000001","Rust","rust","Ingénierie",[33,39,45],{"id":34,"title":35,"slug":36,"excerpt":37,"locale":12,"category_name":31,"published_at":38},"d0000000-0000-0000-0000-000000000677","Pourquoi Bali devient le hub impact-tech d'Asie du Sud-Est en 2026","pourquoi-bali-devient-hub-impact-tech-asie-sud-est-2026","Bali se classe 16e parmi les écosystèmes startups d'Asie du Sud-Est. Avec une concentration croissante de bâtisseurs Web3, de startups IA durables et d'entreprises eco-travel tech, l'île se forge une identité de capitale impact-tech de la région.","2026-03-28T10:44:49.517126Z",{"id":40,"title":41,"slug":42,"excerpt":43,"locale":12,"category_name":31,"published_at":44},"d0000000-0000-0000-0000-000000000676","Le patchwork de la protection des données ASEAN : checklist de conformité pour les développeurs","patchwork-protection-donnees-asean-checklist-conformite-developpeurs","Sept pays de l'ASEAN disposent désormais de lois complètes sur la protection des données, chacune avec des modèles de consentement, des exigences de localisation et des structures de sanctions différents. Voici une checklist pratique de conformité pour les développeurs.","2026-03-28T10:44:49.504560Z",{"id":46,"title":47,"slug":48,"excerpt":49,"locale":12,"category_name":31,"published_at":50},"d0000000-0000-0000-0000-000000000675","La transformation numérique de 29 milliards de dollars d'Indonesia : opportunités pour les éditeurs de logiciels","transformation-numerique-29-milliards-dollars-indonesia-opportunites-editeurs-logiciels","Le marché des services informatiques d'Indonesia devrait atteindre 29,03 milliards de dollars en 2026, contre 24,37 milliards en 2025. L'infrastructure cloud, l'IA, le e-commerce et les centres de données tirent la croissance la plus rapide d'Asie du Sud-Est.","2026-03-28T10:44:49.469231Z",{"id":13,"name":52,"slug":53,"bio":54,"photo_url":19,"linkedin":19,"role":55,"created_at":56,"updated_at":56},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]