#scala #concurrency
#scala #параллелизм
Вопрос:
Я пишу рекурсивную функцию повтора в scala, где я хочу знать, была ли ошибка во время выполнения при создании future. Если есть, то повторяется создание экземпляра future.
Представьте, что у меня есть функция запроса к базе данных:
dbLookup(userId : UserId) : Option[UserName] = ???
Повторная попытка будет выглядеть примерно так:
retry[T](f : () => Future[Option[T]],
notifyFailure : (t : Throwable) => Unit,
n : Int) : Future[Option[T]] = {
if(n <= 0) { Future{None} }
else {
val fut = f()
if(f.resultsInException) { //I don't know how to write this
notifyFailure(f.exception)
retry(f, notifyFailure, n-1) //try again
}
else {
f.originalValueAsFuture
}
}
}
Как эта будущая функциональность может быть реализована и обеспечить оптимизацию хвостовой рекурсии?
Эта функция может использоваться для повторной попытки базы данных 10 раз для пользователя, если контекст выполнения продолжает выдавать исключение при попытке создать Future:
val userNameAfterRetries =
retry(() => Future{dbLookup("1234")},
(t) => system error (s"future creation error : $t"),
10)
Примечание: это вроде как возможно с помощью Future.fallbackTo
, но, к сожалению, fallbackTo принимает Future[T]
вместо () => Future[T]
. Это важно, потому что использование fallbackTo приведет к повторной попытке по крайней мере 1 дополнительный раз, даже если первая попытка была успешной.
Заранее благодарю вас за ваше рассмотрение и ответ.
Комментарии:
1. Можете ли вы использовать «OnFailure» из Future? Может быть, я что-то упускаю?
2. @Jegan
onFailure
может «зарегистрировать» notifyFailure для запуска в случае возникновения сбоя, но тогда мне нужно вернуть значение повторной попытки…
Ответ №1:
Как насчет этого?
def retry[T](f: () => Future[Option[T]],
notifyFailure: Throwable => Unit,
n: Int)(implicit ec : ExecutionContext): Future[Option[T]] = {
if (n <= 0) Future.failed(new RuntimeException("Exceeded number of allowed retries"))
else f().recoverWith { case originalError => notifyFailure(originalError); retry(f, notifyFailure, n - 1) }
}
Обновление по хвостовой рекурсии: Характер Future
асинхронный, поэтому, если вы не хотите await
получить результат, я не совсем понимаю, возможно ли это сделать @tailrec
, потому что вам придется использовать рекурсию при обратном вызове.
Также практическое примечание: если вы знаете, что это всегда ~ 10 попыток, я бы не боялся рекурсии.
Комментарии:
1. ОБНОВЛЕНИЕ: Фактически упрощенное решение до самого крошечного из возможных 🙂
2. Я думаю, вопрос требует рекурсивного решения.
3. На самом деле это так, взгляните на часть «else», она восстанавливает future с помощью рекурсивного вызова метода «retry»
4. На самом деле это не так, попробуйте добавить аннотацию @tailrec к вашему методу. Но меня бы это устроило 🙂 поскольку ‘n’ здесь может быть только 10.
5. Да, вы правы. повторный вызов не находится в конечной позиции. Я могу попытаться придумать хвостовое рекурсивное решение, но, вероятно, позже сегодня 🙂