Реактор проекта: как повторить моно с другим аргументом, пока не будет выполнено какое-то условие

Мне нужно сгенерировать уникальный код асинхронно с реактором проекта.

Сигнатура метода выглядит так:

public Mono<String> generateCode() 

Таким образом, поток должен быть таким:

  1. Сгенерировать случайный код
  2. Проверить, существует ли этот код в базе данных
  3. Если существует, повторно сгенерируйте код (шаг 1) и проверьте его еще раз (шаг 2).
  4. Если код уникален, вернуть его

Мое текущее решение состоит в том, чтобы рекурсивно вызвать generateCode следующим образом:

 Mono<String> generateCode() {
    String code = generateCodeValue();
    return emailConfirmationRepository
        .findByCode(code)
        .flatMap(codeOpt -> codeOpt.map(c -> generateCode()).orElseGet(() -> Mono.just(code)));
  }

Но мне это не нравится, потому что каждый вызов создает свой стек и это может привести к StackOverflowError.

Я знаю, вызовов должно быть очень большое количество, скорее всего этого не будет, но все же, мне нужно решение без рекурсии, как простой цикл while, но с асинхронным кодом.

Как я могу добиться этого с помощью реактора?

Вы можете использовать любой вариант повтора - projectreactor.io/docs/core/release/api/reactor/core/publish‌​er/…

uneq95 09.03.2019 20:12
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
1
1 272
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете вернуть ошибку Mono, когда данный код существует, и использовать оператор retry.

Mono<String> generateCode() {
    return Mono.fromCallable(() -> generateCodeValue())
            .flatMap(code -> emailConfirmationRepository
                    .findByCode(code)
                    .flatMap(codeOpt -> codeOpt
                            .map(c -> Mono.<String>error(new CodeAlreadyExistsException()))
                            .orElseGet(() -> Mono.just(code))))
            .retry(5);
}

class CodeAlreadyExistsException extends RuntimeException {}

Обратите внимание, однако, что повторная попытка повторит все ваши шаги. Итак, если у вас есть более сложный код, например:

Mono<String> generateCode() {
return Mono.fromCallable(() -> generateCodeValue())
        .flatMap(code -> doSomeExpensiveOperation1())
        .flatMap(code -> doSomeDangerousOperation2())
        .flatMap(code -> emailConfirmationRepository
                .findByCode(code)
                .flatMap(codeOpt -> codeOpt
                        .map(c -> Mono.<String>error(new CodeAlreadyExistsException()))
                        .orElseGet(() -> Mono.just(code))))
        .retry(5);
}
class CodeAlreadyExistsException extends RuntimeException {}

Затем все ваши шаги до «findByCode» будут повторяться снова, включая doSomeExpensiveOperation1 и doSomeDangerousOperation2.

Хотя он выполнит свою работу, но мне любопытно, как повторить попытку, пока не будет выполнено НЕКОТОРОЕ УСЛОВИЕ (без ограничения повторных попыток)

Teimuraz 10.03.2019 17:08

@RodneyZ есть ли способ повторить только одну «предыдущую» операцию в цепочке?

NikolaB 24.12.2019 12:23
Ответ принят как подходящий

Чтобы повторить попытку ДО ВЫПОЛНЕНИЯ НЕКОТОРОГО УСЛОВИЯ на неопределенный срок, вы должны:

Mono<String> generateCode() {
return Mono.fromCallable(() -> generateCodeValue())
        .flatMap(code -> emailConfirmationRepository
                .findByCode(code)
                .flatMap(codeOpt -> codeOpt
                        .map(c -> Mono.<String>error(new CodeAlreadyExistsException()))
                        .orElseGet(() -> Mono.just(code))))
        .retry(CodeAlreadyExistsException.class::isInstance)
}

class CodeAlreadyExistsException extends RuntimeException {}

Спасибо @alexander-pankin.

Спасибо, @RodneyZ, это именно то, что мне нужно!

Teimuraz 12.03.2019 17:41

Другие вопросы по теме