Scala - 最终超时和 Thread.sleep() 之间的区别
Scala - differents between eventually timeout and Thread.sleep()
我有一些异步 (ZIO) 代码,我需要对其进行测试。如果我使用 Thread.sleep()
创建一个测试部分,它工作正常并且我总是得到响应:
for {
saved <- database.save(smth)
result <- eventually {
Thread.sleep(20000)
database.search(...)
}
} yield result
但是,如果我使用 timeout
和 eventually
中的 interval
进行相同的逻辑,那么它永远不会正常工作(我超时):
for {
saved <- database.save(smth)
result <- eventually(timeout(Span(20, Seconds)), interval(Span(20, Seconds))) {
database.search(...)
}
} yield result
我不明白为什么 timeout
和 interval
与 Thread.sleep
的工作方式不同。它应该做完全相同的事情。有人可以向我解释一下并告诉我应该如何更改此代码以不需要使用 Thread.sleep()
?
假设 database.search(...)
returns ZIO[]
对象。
eventually{database.search(...)}
很可能在第一次尝试后立即成功。
它成功创建了一个查询数据库的任务。
然后在没有任何重试逻辑的情况下查询数据库。
关于如何让它发挥作用:
val search: ZIO[Any, Throwable, String] = ???
val retried: ZIO[Any with Clock, Throwable, Option[String]] = search.retry(Schedule.spaced(Duration.fromMillis(1000))).timeout(Duration.fromMillis(20000))
类似的东西应该有用。但我相信存在更优雅的解决方案。
@simpadjo 的另一个回答非常简洁地解决了“什么”问题。我将添加一些额外的上下文来说明您为什么会看到这种行为。
for {
saved <- database.save(smth)
result <- eventually {
Thread.sleep(20000)
database.search(...)
}
} yield result
这里混合了三种不同的技术,这引起了一些混乱。
首先是 ZIO which is an asynchronous programming library that uses it's own custom runtime and execution model to perform tasks. The second is eventually
which comes from ScalaTest,可用于通过有效轮询值的状态来检查异步计算。第三,Thread.sleep
是一个 Java api,它实际上是挂起当前线程并阻止任务继续进行,直到计时器到期。
eventually
使用一种简单的重试机制,该机制根据您使用的是普通值还是来自 scala 标准库的 Future
而有所不同。基本上它运行块中的代码,如果它抛出,则它使当前线程休眠,然后根据一些间隔配置重试它,最终超时。值得注意的是,在这种情况下,行为是完全同步的,这意味着只要 {}
中的值不引发异常,它就不会继续重试。
Thread.sleep
是一个重量级操作,在这种情况下,它有效地阻止了传递给 eventually
的函数进行 20 秒。这意味着在调用 database.search
时操作可能已经完成。
第二个变体不同,它会立即执行eventually
块中的代码,如果抛出异常,则会根据您提供的interval/timeout逻辑再次尝试。在这种情况下,保存可能尚未完成(如果最终一致,则可能未传播)。因为你 returning 一个 ZIO
被设计 不是 抛出,并且 eventually
不理解 ZIO
它只会return search
尝试没有重试逻辑。
接受的答案:
val retried: ZIO[Any with Clock, Throwable, Option[String]] = search.retry(Schedule.spaced(Duration.fromMillis(1000))).timeout(Duration.fromMillis(20000))
之所以有效,是因为 retry
和 timeout
正在使用内置的 ZIO
运算符,do 了解如何实际 retry
和 timeout
一个 ZIO
。这意味着如果搜索失败,retry
将处理它直到成功。
我有一些异步 (ZIO) 代码,我需要对其进行测试。如果我使用 Thread.sleep()
创建一个测试部分,它工作正常并且我总是得到响应:
for {
saved <- database.save(smth)
result <- eventually {
Thread.sleep(20000)
database.search(...)
}
} yield result
但是,如果我使用 timeout
和 eventually
中的 interval
进行相同的逻辑,那么它永远不会正常工作(我超时):
for {
saved <- database.save(smth)
result <- eventually(timeout(Span(20, Seconds)), interval(Span(20, Seconds))) {
database.search(...)
}
} yield result
我不明白为什么 timeout
和 interval
与 Thread.sleep
的工作方式不同。它应该做完全相同的事情。有人可以向我解释一下并告诉我应该如何更改此代码以不需要使用 Thread.sleep()
?
假设 database.search(...)
returns ZIO[]
对象。
eventually{database.search(...)}
很可能在第一次尝试后立即成功。
它成功创建了一个查询数据库的任务。 然后在没有任何重试逻辑的情况下查询数据库。
关于如何让它发挥作用:
val search: ZIO[Any, Throwable, String] = ???
val retried: ZIO[Any with Clock, Throwable, Option[String]] = search.retry(Schedule.spaced(Duration.fromMillis(1000))).timeout(Duration.fromMillis(20000))
类似的东西应该有用。但我相信存在更优雅的解决方案。
@simpadjo 的另一个回答非常简洁地解决了“什么”问题。我将添加一些额外的上下文来说明您为什么会看到这种行为。
for {
saved <- database.save(smth)
result <- eventually {
Thread.sleep(20000)
database.search(...)
}
} yield result
这里混合了三种不同的技术,这引起了一些混乱。
首先是 ZIO which is an asynchronous programming library that uses it's own custom runtime and execution model to perform tasks. The second is eventually
which comes from ScalaTest,可用于通过有效轮询值的状态来检查异步计算。第三,Thread.sleep
是一个 Java api,它实际上是挂起当前线程并阻止任务继续进行,直到计时器到期。
eventually
使用一种简单的重试机制,该机制根据您使用的是普通值还是来自 scala 标准库的 Future
而有所不同。基本上它运行块中的代码,如果它抛出,则它使当前线程休眠,然后根据一些间隔配置重试它,最终超时。值得注意的是,在这种情况下,行为是完全同步的,这意味着只要 {}
中的值不引发异常,它就不会继续重试。
Thread.sleep
是一个重量级操作,在这种情况下,它有效地阻止了传递给 eventually
的函数进行 20 秒。这意味着在调用 database.search
时操作可能已经完成。
第二个变体不同,它会立即执行eventually
块中的代码,如果抛出异常,则会根据您提供的interval/timeout逻辑再次尝试。在这种情况下,保存可能尚未完成(如果最终一致,则可能未传播)。因为你 returning 一个 ZIO
被设计 不是 抛出,并且 eventually
不理解 ZIO
它只会return search
尝试没有重试逻辑。
接受的答案:
val retried: ZIO[Any with Clock, Throwable, Option[String]] = search.retry(Schedule.spaced(Duration.fromMillis(1000))).timeout(Duration.fromMillis(20000))
之所以有效,是因为 retry
和 timeout
正在使用内置的 ZIO
运算符,do 了解如何实际 retry
和 timeout
一个 ZIO
。这意味着如果搜索失败,retry
将处理它直到成功。