Skip to content

feat: add wrap_with_*_context and deserialize_to_snapshot APIs (v0.8.1)#26

Open
Yaming-Hub wants to merge 5 commits into
mainfrom
feat/wrap-and-deserialize-apis
Open

feat: add wrap_with_*_context and deserialize_to_snapshot APIs (v0.8.1)#26
Yaming-Hub wants to merge 5 commits into
mainfrom
feat/wrap-and-deserialize-apis

Conversation

@Yaming-Hub
Copy link
Copy Markdown
Collaborator

Summary

Adds two new API categories that close gaps in the context inheritance model:

1. Wrap helpers — capture context without spawning

Function Source Description
wrap_with_sync_context(mode, fut) sync (thread-local) Returns a future with task-local context
wrap_with_async_context(mode, fut) async (task-local) Returns a future with task-local context

These decouple context capture from task spawning. The existing spawn_with_* helpers always call tokio::spawn immediately, but many patterns need to capture context now and spawn later:

  • Task queues / backpressure — enqueue a context-carrying future on a bounded channel
  • Retry wrappers — capture context once, create wrapped futures for each retry
  • Rate limiters — capture at request time, execute when a slot opens
  • Priority queues — wrap, assign priority, spawn highest-priority first

Both support Fork (cheap Arc sharing) and Snapshot (full copy) modes.

2. deserialize_to_snapshot — wire bytes → ContextSnapshot directly

pub fn deserialize_to_snapshot(bytes: &[u8]) -> Result<ContextSnapshot, ContextError>

Converts wire bytes directly into a ContextSnapshot without mutating any live context store. Previously, the only way was a three-step dance:

// Before: deserialize into live store, snapshot, then drop
let _guard = sync_ctx::deserialize_context(&bytes)?;
let snap = sync_ctx::snapshot();
drop(_guard);
// After: direct conversion, no side effects
let snap = dcontext::deserialize_to_snapshot(&bytes)?;

Benefits:

  • No thread-local/task-local mutation
  • Single pass (no double work)
  • Works on any thread (no task-local store required)

Changes

  • dcontext/src/inheritance.rs — added wrap_with_sync_context, wrap_with_async_context
  • dcontext/src/wire.rs — added deserialize_to_snapshot
  • dcontext/src/lib.rs — re-exports for new APIs
  • dcontext/src/tests.rs — 9 new tests (5 wrap, 4 deserialize)
  • dcontext/Cargo.toml — version bump 0.8.0 → 0.8.1

Test Results

All 88 tests pass, zero clippy warnings.

Yaming-Hub and others added 5 commits May 12, 2026 15:57
Add two wrap helpers that capture context without spawning:
- wrap_with_sync_context(mode, fut) — sync → async context wrapping
- wrap_with_async_context(mode, fut) — async → async context wrapping

These decouple capture from spawn, enabling enqueue-then-spawn,
retry, rate-limiting, and priority queue patterns.

Add deserialize_to_snapshot(bytes) that converts wire bytes directly
to a ContextSnapshot without mutating any live context store.
Useful for sync actors, message routers, and context inspection.

Bump version: 0.8.0 → 0.8.1

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a clear semantic API to check if a task-local context store is
available, replacing the pattern async_ctx::current_depth().is_some().

Updated SyncDcontextLayer to use the new API.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Usage guide §5: rewrite with ContextInheritance modes, spawn helpers,
  wrap helpers, message passing guidance, deserialize_to_snapshot
- Design doc §4.4-4.7: update root-level helpers, add Fork vs Snapshot
  guidance, wrap helper patterns, direct wire deserialization
- Design doc §11.2-11.6: update examples to use new API names,
  add cross-thread message passing pattern, use deserialize_to_snapshot
  in HTTP middleware example

Key guidance: use Fork for parent-child task relationships, Snapshot
for cross-thread message passing where sender's context may be gone.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds attach_named() that accepts a scope name parameter, making the
attached scope visible in scope_chain() for debugging and tracing.
The existing attach() remains unchanged (unnamed scope).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The layer no longer pushes/pops dcontext scopes per span enter/exit.
Scope lifecycle is now entirely the caller's responsibility. This avoids
issues with span/scope lifetime mismatches in async code and eliminates
unnecessary scope overhead.

The layer retains its core value as a span<->context data bridge:
- Field extraction (span fields -> context values)
- Span info (span metadata -> context value)
- Span recording (context values -> span fields)
- Log enrichment (context values -> log events)

Removed guard_stack.rs (only used for scope guards).
Removed 13 scoping tests, added 2 no-scoping behavior tests.
Updated README, lib.rs docs, design doc.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant