Skip to content

Using TaskLocal in Interceptor Chain #3597

@PatrickDanino

Description

@PatrickDanino

Question

General question to try to clarify with more specificity how @TaskLocal is supposed to help us track context across interceptors. I'll caveat my questions by admitting that my experience with this is limited.

The examples that are in the docs seem somewhat constrained. More specifically, they all seem to assume that you have the data available at the start of the chain, not somewhere further down the chain. My understand of a limitation of @TaskLocal is that it only goes from Parent to child, not from child back to parent.

Said another way, unless you define your @TaskLocal at the very start of the entire interceptor chain, it's unlikely to be useful somewhere else because you otherwise have no way of being sure that async task has the context already defined.

Separately, it's not even clear to me that if you succeed in defining the @TaskLocal, it'll actually make its way down. This is in part due to code like this throughout the interceptors:

extension AsyncThrowingStream where Failure == any Swift.Error {
  static func executingInAsyncTask(
    bufferingPolicy: AsyncThrowingStream<Element, Failure>.Continuation.BufferingPolicy = .unbounded,
    _ block: @escaping @Sendable (Continuation) async throws -> Void
  ) -> Self {
    return AsyncThrowingStream(bufferingPolicy: bufferingPolicy) { continuation in
      let task = Task {
        do {
          try await block(continuation)
          continuation.finish()

        } catch {
          continuation.finish(throwing: error)
        }
      }

      continuation.onTermination = { _ in task.cancel() }
    }
  }
}

My (limited) understanding of this snippet is that the code being executed in the try await block(continuation) will not inherit the parent context due to the explicit Task { ... } declaration from within a continuation block. Given AsyncThrowingStream.executingInAsyncTask is being called in numerous places, all these blocks have their own task/context that isn't shared.

Am I misunderstanding or missing something? I'll admit it's still unclear to me how @TaskLocal can help expose the HTTP headers available in the GQL response given it's made available so late in the interceptor chain.

Metadata

Metadata

Assignees

Labels

questionIssues that have a question which should be addressed

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions