[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-context-propagation-async-rust-deadline-pembatalan-tracing":3},{"article":4,"author":52},{"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":7,"meta_description":16,"focus_keyword":17,"og_image":18,"canonical_url":18,"robots_meta":19,"created_at":15,"updated_at":15,"tags":20,"category_name":30,"related_articles":31},"d2000000-0000-0000-0000-000000000124","a0000000-0000-0000-0000-000000000026","Context Propagation di Async Rust — Deadline, Pembatalan, dan Tracing","context-propagation-async-rust-deadline-pembatalan-tracing","Mengelola context di async Rust: propagasi deadline dan timeout, pembatalan kooperatif dengan CancellationToken, dan distributed tracing dengan span.","## Masalah Context di Async Rust\n\nDalam aplikasi async, satu request HTTP memicu rantai operasi: query database, panggilan API eksternal, komputasi, dan lainnya. Setiap operasi ini perlu mengetahui tentang context request: berapa waktu tersisa (deadline), apakah request sudah dibatalkan, dan ID request untuk tracing.\n\nRust tidak memiliki implicit context seperti Go's `context.Context`. Kita harus mengelolanya secara eksplisit — tetapi hasilnya lebih type-safe.\n\n## Deadline dan Timeout\n\n### tokio::time::timeout\n```rust\nuse tokio::time::{timeout, Duration};\n\nasync fn fetch_with_timeout(url: &str) -> Result\u003CResponse, AppError> {\n    timeout(Duration::from_secs(5), reqwest::get(url))\n        .await\n        .map_err(|_| AppError::Timeout)?\n        .map_err(AppError::from)\n}\n```\n\n### Propagasi Deadline\n```rust\nstruct RequestContext {\n    deadline: Instant,\n    request_id: Uuid,\n    span: tracing::Span,\n}\n\nimpl RequestContext {\n    fn remaining(&self) -> Duration {\n        self.deadline.saturating_duration_since(Instant::now())\n    }\n    \n    fn is_expired(&self) -> bool {\n        Instant::now() >= self.deadline\n    }\n}\n\nasync fn process_order(ctx: &RequestContext, order: Order) -> Result\u003C(), AppError> {\n    if ctx.is_expired() {\n        return Err(AppError::DeadlineExceeded);\n    }\n    \n    \u002F\u002F Propagasi sisa waktu ke sub-operasi\n    let remaining = ctx.remaining();\n    timeout(remaining, save_to_database(&order)).await??;\n    \n    let remaining = ctx.remaining();\n    timeout(remaining, notify_warehouse(&order)).await??;\n    \n    Ok(())\n}\n```\n\n## Pembatalan Kooperatif\n\n### CancellationToken\n```rust\nuse tokio_util::sync::CancellationToken;\n\nasync fn long_running_task(cancel: CancellationToken) {\n    loop {\n        tokio::select! {\n            _ = cancel.cancelled() => {\n                tracing::info!(\"Task dibatalkan\");\n                cleanup().await;\n                return;\n            }\n            result = do_work() => {\n                match result {\n                    Ok(()) => continue,\n                    Err(e) => {\n                        tracing::error!(\"Error: {:?}\", e);\n                        return;\n                    }\n                }\n            }\n        }\n    }\n}\n\n\u002F\u002F Di handler HTTP:\nasync fn handle_request() -> Response {\n    let cancel = CancellationToken::new();\n    let child_cancel = cancel.child_token();\n    \n    let task = tokio::spawn(long_running_task(child_cancel));\n    \n    \u002F\u002F Jika client disconnect, batalkan task\n    tokio::select! {\n        result = task => { \u002F* task selesai *\u002F }\n        _ = client_disconnected() => {\n            cancel.cancel();\n        }\n    }\n}\n```\n\n## Distributed Tracing\n\n```rust\nuse tracing::{instrument, info_span, Instrument};\n\n#[instrument(skip(pool), fields(request_id = %request_id))]\nasync fn handle_order(\n    pool: &PgPool,\n    request_id: Uuid,\n    order: CreateOrder,\n) -> Result\u003COrder, AppError> {\n    let order = create_order(pool, &order)\n        .instrument(info_span!(\"db.create_order\"))\n        .await?;\n    \n    send_notification(&order)\n        .instrument(info_span!(\"notify.send\"))\n        .await?;\n    \n    Ok(order)\n}\n```\n\n### Integrasi dengan OpenTelemetry\n```rust\nuse tracing_subscriber::prelude::*;\nuse tracing_opentelemetry::OpenTelemetryLayer;\n\nfn setup_tracing() {\n    let tracer = opentelemetry_jaeger::new_agent_pipeline()\n        .with_service_name(\"my-api\")\n        .install_simple()\n        .unwrap();\n    \n    tracing_subscriber::registry()\n        .with(tracing_subscriber::fmt::layer())\n        .with(OpenTelemetryLayer::new(tracer))\n        .init();\n}\n```\n\n## Middleware Axum untuk Context\n\n```rust\nuse axum::middleware::{self, Next};\n\nasync fn context_middleware(\n    mut request: Request,\n    next: Next,\n) -> Response {\n    let request_id = Uuid::new_v4();\n    let deadline = Instant::now() + Duration::from_secs(30);\n    \n    let ctx = RequestContext {\n        deadline,\n        request_id,\n        span: info_span!(\"request\", id = %request_id),\n    };\n    \n    request.extensions_mut().insert(ctx);\n    \n    let response = next.run(request)\n        .instrument(info_span!(\"http\", id = %request_id))\n        .await;\n    \n    response\n}\n```\n\n## Kesimpulan\n\nContext propagation di async Rust memerlukan pendekatan eksplisit: struct RequestContext untuk deadline, CancellationToken untuk pembatalan kooperatif, dan tracing spans untuk distributed tracing. Meskipun memerlukan lebih banyak kode dari implicit context, hasilnya lebih type-safe dan lebih mudah di-debug.","\u003Ch2 id=\"masalah-context-di-async-rust\">Masalah Context di Async Rust\u003C\u002Fh2>\n\u003Cp>Dalam aplikasi async, satu request HTTP memicu rantai operasi: query database, panggilan API eksternal, komputasi, dan lainnya. Setiap operasi ini perlu mengetahui tentang context request: berapa waktu tersisa (deadline), apakah request sudah dibatalkan, dan ID request untuk tracing.\u003C\u002Fp>\n\u003Cp>Rust tidak memiliki implicit context seperti Go’s \u003Ccode>context.Context\u003C\u002Fcode>. Kita harus mengelolanya secara eksplisit — tetapi hasilnya lebih type-safe.\u003C\u002Fp>\n\u003Ch2 id=\"deadline-dan-timeout\">Deadline dan Timeout\u003C\u002Fh2>\n\u003Ch3>tokio::time::timeout\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-rust\">use tokio::time::{timeout, Duration};\n\nasync fn fetch_with_timeout(url: &amp;str) -&gt; Result&lt;Response, AppError&gt; {\n    timeout(Duration::from_secs(5), reqwest::get(url))\n        .await\n        .map_err(|_| AppError::Timeout)?\n        .map_err(AppError::from)\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Propagasi Deadline\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-rust\">struct RequestContext {\n    deadline: Instant,\n    request_id: Uuid,\n    span: tracing::Span,\n}\n\nimpl RequestContext {\n    fn remaining(&amp;self) -&gt; Duration {\n        self.deadline.saturating_duration_since(Instant::now())\n    }\n    \n    fn is_expired(&amp;self) -&gt; bool {\n        Instant::now() &gt;= self.deadline\n    }\n}\n\nasync fn process_order(ctx: &amp;RequestContext, order: Order) -&gt; Result&lt;(), AppError&gt; {\n    if ctx.is_expired() {\n        return Err(AppError::DeadlineExceeded);\n    }\n    \n    \u002F\u002F Propagasi sisa waktu ke sub-operasi\n    let remaining = ctx.remaining();\n    timeout(remaining, save_to_database(&amp;order)).await??;\n    \n    let remaining = ctx.remaining();\n    timeout(remaining, notify_warehouse(&amp;order)).await??;\n    \n    Ok(())\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"pembatalan-kooperatif\">Pembatalan Kooperatif\u003C\u002Fh2>\n\u003Ch3>CancellationToken\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-rust\">use tokio_util::sync::CancellationToken;\n\nasync fn long_running_task(cancel: CancellationToken) {\n    loop {\n        tokio::select! {\n            _ = cancel.cancelled() =&gt; {\n                tracing::info!(\"Task dibatalkan\");\n                cleanup().await;\n                return;\n            }\n            result = do_work() =&gt; {\n                match result {\n                    Ok(()) =&gt; continue,\n                    Err(e) =&gt; {\n                        tracing::error!(\"Error: {:?}\", e);\n                        return;\n                    }\n                }\n            }\n        }\n    }\n}\n\n\u002F\u002F Di handler HTTP:\nasync fn handle_request() -&gt; Response {\n    let cancel = CancellationToken::new();\n    let child_cancel = cancel.child_token();\n    \n    let task = tokio::spawn(long_running_task(child_cancel));\n    \n    \u002F\u002F Jika client disconnect, batalkan task\n    tokio::select! {\n        result = task =&gt; { \u002F* task selesai *\u002F }\n        _ = client_disconnected() =&gt; {\n            cancel.cancel();\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"distributed-tracing\">Distributed Tracing\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">use tracing::{instrument, info_span, Instrument};\n\n#[instrument(skip(pool), fields(request_id = %request_id))]\nasync fn handle_order(\n    pool: &amp;PgPool,\n    request_id: Uuid,\n    order: CreateOrder,\n) -&gt; Result&lt;Order, AppError&gt; {\n    let order = create_order(pool, &amp;order)\n        .instrument(info_span!(\"db.create_order\"))\n        .await?;\n    \n    send_notification(&amp;order)\n        .instrument(info_span!(\"notify.send\"))\n        .await?;\n    \n    Ok(order)\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Integrasi dengan OpenTelemetry\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-rust\">use tracing_subscriber::prelude::*;\nuse tracing_opentelemetry::OpenTelemetryLayer;\n\nfn setup_tracing() {\n    let tracer = opentelemetry_jaeger::new_agent_pipeline()\n        .with_service_name(\"my-api\")\n        .install_simple()\n        .unwrap();\n    \n    tracing_subscriber::registry()\n        .with(tracing_subscriber::fmt::layer())\n        .with(OpenTelemetryLayer::new(tracer))\n        .init();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"middleware-axum-untuk-context\">Middleware Axum untuk Context\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">use axum::middleware::{self, Next};\n\nasync fn context_middleware(\n    mut request: Request,\n    next: Next,\n) -&gt; Response {\n    let request_id = Uuid::new_v4();\n    let deadline = Instant::now() + Duration::from_secs(30);\n    \n    let ctx = RequestContext {\n        deadline,\n        request_id,\n        span: info_span!(\"request\", id = %request_id),\n    };\n    \n    request.extensions_mut().insert(ctx);\n    \n    let response = next.run(request)\n        .instrument(info_span!(\"http\", id = %request_id))\n        .await;\n    \n    response\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"kesimpulan\">Kesimpulan\u003C\u002Fh2>\n\u003Cp>Context propagation di async Rust memerlukan pendekatan eksplisit: struct RequestContext untuk deadline, CancellationToken untuk pembatalan kooperatif, dan tracing spans untuk distributed tracing. Meskipun memerlukan lebih banyak kode dari implicit context, hasilnya lebih type-safe dan lebih mudah di-debug.\u003C\u002Fp>\n","id","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:25.143369Z","Context di async Rust: propagasi deadline, pembatalan kooperatif CancellationToken, dan distributed tracing dengan OpenTelemetry.","async Rust context",null,"index, follow",[21,26],{"id":22,"name":23,"slug":24,"created_at":25},"c0000000-0000-0000-0000-000000000022","Performance","performance","2026-03-28T10:44:21.513630Z",{"id":27,"name":28,"slug":29,"created_at":25},"c0000000-0000-0000-0000-000000000001","Rust","rust","Rekayasa",[32,39,46],{"id":33,"title":34,"slug":35,"excerpt":36,"locale":12,"category_name":37,"published_at":38},"d0000000-0000-0000-0000-000000000642","WASI 0.3 dan Kematian Cold Start: Wasm Sisi Server di Produksi","wasi-0-3-kematian-cold-start-wasm-sisi-server-di-produksi","WASI 0.3 dirilis pada Februari 2026 dengan async I\u002FO native, tipe stream, dan dukungan socket penuh. WebAssembly sisi server kini menghadirkan cold start dalam hitungan mikrodetik, dan setiap penyedia cloud besar menawarkan Wasm serverless.","DevOps","2026-03-28T10:44:47.445780Z",{"id":40,"title":41,"slug":42,"excerpt":43,"locale":12,"category_name":44,"published_at":45},"d0000000-0000-0000-0000-000000000620","Stack Backend Modern 2026: Rust + PostgreSQL 18 + Wasm + eBPF","stack-backend-modern-2026-rust-postgresql-wasm-ebpf","Empat teknologi konvergen untuk mendefinisikan ulang infrastruktur backend di 2026: Rust menghilangkan overhead garbage collection dan mengurangi jumlah container hingga 3x, PostgreSQL 18 menggantikan database khusus, WASI 0.3 memberikan cold start mikrodetik untuk fungsi serverless, dan eBPF memungkinkan observabilitas tanpa instrumentasi dengan biaya yang jauh lebih rendah dari monitoring tradisional.","Engineering","2026-03-28T10:44:45.804120Z",{"id":47,"title":48,"slug":49,"excerpt":50,"locale":12,"category_name":30,"published_at":51},"d0000000-0000-0000-0000-000000000548","Dari Autocomplete ke Otonom: Evolusi Alat AI Coding (2022-2026)","dari-autocomplete-ke-otonom-evolusi-alat-ai-coding-2022-2026","Kronik tentang bagaimana alat AI coding berevolusi dari autocomplete satu baris di 2022 menjadi agen multi-file otonom di 2026. Empat tahun yang mengubah pengembangan perangkat lunak selamanya, dengan pandangan tentang apa yang akan datang.","2026-03-28T10:44:41.351824Z",{"id":13,"name":53,"slug":54,"bio":55,"photo_url":18,"linkedin":18,"role":56,"created_at":57,"updated_at":57},"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"]