javaredisspring-webfluxproject-reactor

Is it right to call subscribe within async context?


I store encryption keys in a Redis database. I want to implement a fallback mechanism to generate keys unless they are already present. What confuses me with a solution I come up with is that I call subscribe within an async context. My IDE pointed me this problem. So the question is how would you implement this fallback?

public Mono<String> getEncryptionKey(String entity) {
    return reactiveRedisOperations.opsForValue()
            .get("private")
            .switchIfEmpty(
                    Mono.create(stringMonoSink -> {
                        try {
                            var privateKeyEncoded = ...
                            var publicKeyEncoded = ...

                            reactiveRedisOperations.opsForValue()
                                    .multiSet(Map.of(
                                            "private", privateKeyEncoded,
                                            "public", publicKeyEncoded
                                    ))
                                    .subscribe(success -> {
                                        stringMonoSink.success(privateKeyEncoded);
                                    });
                        } catch (Throwable e) {
                            stringMonoSink.error(e);
                        }
                    })
            );
}

Solution

  • You can use Mono.defer(Supplier<? extends Mono<? extends T>> supplier). It takes a supplier for Mono, that will be called only if Mono subscribed. In your case it means key generation will be called only in case of switchIfEmpty:

        return reactiveRedisOperations.opsForValue()
                .get("private")
                .switchIfEmpty(Mono.defer(() -> {
                            var privateKeyEncoded = "...";
                            var publicKeyEncoded = "...";
                            return reactiveRedisOperations.opsForValue()
                                    .multiSet(Map.of(
                                            "private", privateKeyEncoded,
                                            "public", publicKeyEncoded
                                    ))
                                    .map(b -> {
                                        if (b) return privateKeyEncoded;
                                        else throw new RuntimeException();
                                    });
                        })
                );