[英]debugging scala futures - how to determine the future's execution context
related with another question I posted ( scala futures - keeping track of request context when threadId is irrelevant ) when debugging a future, the call stack isn't very informative (as the call context is usually in another thread and another time).与我发布的另一个问题( scala futures - 在 threadId 无关时跟踪请求上下文)相关,在调试未来时,调用堆栈的信息量不是很大(因为调用上下文通常在另一个线程和另一个时间)。 this is especially problematic when there can be different paths leading to the same future code (for instance usage of DAO called from many places in the code etc).
当可能有不同的路径导致相同的未来代码时,这尤其成问题(例如使用从代码中的许多地方调用的 DAO 等)。 do you know of an elegant solution for this?
你知道一个优雅的解决方案吗? I was thinking of passing a token/request ID (for flows started by a web server request) - but this would require passing it around - and also won't include any of the state which you can see in the stack trace.
我正在考虑传递令牌/请求 ID(对于由 Web 服务器请求启动的流) - 但这需要传递它 - 并且也不包括您可以在堆栈跟踪中看到的任何状态。 perhaps passing a stack around?
也许传递一个堆栈? :)
:)
Suppose you make a class假设你创建一个类
case class Context(requestId: Int, /* other things you need to pass around */)
There are two basic ways to send it around implicitly:有两种基本方法可以隐式发送它:
1) Add an implicit Context
parameter to any function that requires it: 1) 向任何需要它的函数添加一个隐式
Context
参数:
def processInAnotherThread(/* explicit arguments */)(
implicit evaluationContext: scala.concurrent.EvaluationContext,
context: Context): Future[Result] = ???
def processRequest = {
/* ... */
implicit val context: Context = Context(getRequestId, /* ... */)
processInAnotherThread(/* explicit parameters */)
}
The drawback is that every function that needs to access Context
must have this parameter and it litters the function signatures quite a bit.缺点是每个需要访问
Context
的函数都必须有这个参数,而且它会大量占用函数签名。
2) Put it into a DynamicVariable
: 2) 将其放入
DynamicVariable
:
// Context companion object
object Context {
val context: DynamicVariable[Context] =
new DynamicVariable[Context](Context(0, /* ... */))
}
def processInAnotherThread(/* explicit arguments */)(
implicit evaluationContext: scala.concurrent.EvaluationContext
): Future[Result] = {
// get requestId from context
Context.context.value.requestId
/* ... */
}
def processRequest = {
/* ... */
Context.context.withValue(Context(getRequestId, /* ... */)) {
processInAnotherThread(/* explicit parameters */)
}
}
The drawbacks are that缺点是
DynamicVariable
s, preferably don't have more than 1 or at most 2 and document their use.DynamicVariable
的数量,最好不要超过 1 或最多 2 并记录它们的使用。null
s for all its contents, or it must itself be a null
by default ( new DynamicVariable[Context](null)
). null
,或者默认情况下它本身必须为null
( new DynamicVariable[Context](null)
)。 Forgetting to initialize Context
or its contents before processing may lead to nasty errors.Context
或其内容可能会导致严重的错误。 DynamicVariable
is still much better than some global variable and doesn't influence the signatures of the functions that don't use it directly in any way. DynamicVariable
仍然比某些全局变量好得多,并且不会影响不以任何方式直接使用它的函数的签名。
In both cases you may update the contents of an existing Context
with a copy
method of a case class
.在这两种情况下,您都可以使用
case class
的copy
方法更新现有Context
的内容。 For example:例如:
def deepInProcessing(/* ... */): Future[Result] =
Context.context.withValue(
Context.context.value.copy(someParameter = newParameterValue)
) {
processFurther(/* ... */)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.