{"id":110989,"date":"2021-08-08T15:15:00","date_gmt":"2021-08-08T12:15:00","guid":{"rendered":"https:\/\/www.javacodegeeks.com\/?p=110989"},"modified":"2021-08-03T14:18:00","modified_gmt":"2021-08-03T11:18:00","slug":"clever-cache-for-reactors-mono-objects","status":"publish","type":"post","link":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html","title":{"rendered":"Clever cache for Reactor\u2019s Mono objects"},"content":{"rendered":"<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">Data caching is a widespread technique in the programming. It allows to quickly retrieve data without making long-running operations. But there is a problem with caching of data retrieved as result of some long-running operation. If a cache value is missed, it will be requested. If it is requested by a long-running HTTP request or SQL command, the next request for the cache value can leads to multiple HTTP requests \/ SQL commands again and again. I was looking for a cache implementation which solves this issue in projects using <a href=\"https:\/\/projectreactor.io\/\" rel=\"noopener nofollow\">Project Reactor<\/a>. Project Reactor is built on top of the Reactive Streams Specification \u2014 a standard for building reactive applications. You probably know <code>Mono<\/code> and <code>Flux<\/code> objects from <a href=\"https:\/\/docs.spring.io\/spring-framework\/docs\/current\/reference\/html\/web-reactive.html\" rel=\"noopener nofollow\">Spring WebFlux<\/a>. Project Reactor is the reactive library of choice for Spring WebFlux.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2021\/08\/0_qWwDyYf1PZwTfJl9.jpg\"><img decoding=\"async\" src=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2021\/08\/0_qWwDyYf1PZwTfJl9-1024x683.jpg\" alt=\"\" class=\"wp-image-111009\" width=\"512\" height=\"342\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2021\/08\/0_qWwDyYf1PZwTfJl9-1024x683.jpg 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2021\/08\/0_qWwDyYf1PZwTfJl9-300x200.jpg 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2021\/08\/0_qWwDyYf1PZwTfJl9-768x512.jpg 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2021\/08\/0_qWwDyYf1PZwTfJl9.jpg 1400w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/a><\/figure>\n<\/div>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">In this article, I will suggest a reactive cache implementation inspired by <code>CacheMono<\/code> from Reactor\u2019s addons project. We will assume, that the result of a long-running HTTP request or SQL command is represented as a<code>Mono<\/code> object. A <code>Mono<\/code> object is \u201cmaterialized\u201d and cached in form of Reactor\u2019s <code><a href=\"https:\/\/projectreactor.io\/docs\/core\/release\/api\/reactor\/core\/publisher\/Signal.html\" rel=\"noopener nofollow\">Signal<\/a><\/code> object which represents a <code>Mono<\/code>. Signals are \u201cdematerialized\u201d to Mono\u2019s if a cache value is requested by the <code>lookup<\/code> method. Multiple lookups with the same key will retieve the same <code>Mono<\/code> object, so that a long-running operation is only triggered once!<\/p>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">Let\u2019s create a class <code>CacheMono<\/code> with three factory methods.<\/p>\n<pre class=\"brush:java\">\n@Slf4j\npublic class CacheMono&lt;KEY, IVALUE, OVALUE&gt; {\n\n    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();\n    private final Map&lt;KEY, CacheMonoValue&lt;OVALUE&gt;&gt; cache = new HashMap&lt;&gt;();\n\n    \/**\n     * External value supplier which should be provided if \"valuePublisher\" with \"keyExtractor\"\n     * are not set\n     *\/\n    private final Function&lt;KEY, Mono&lt;OVALUE&gt;&gt; valueSupplier;\n    \/**\n     * External source publisher stream which should be provided if \"valueSupplier\" is not set\n     *\/\n    private final Flux&lt;IVALUE&gt; valuePublisher;\n    \/**\n     * Key extractor for emitted items provided by \"valuePublisher\"\n     *\/\n    private final Function&lt;IVALUE, KEY&gt; keyExtractor;\n    \/**\n     * Value extractor for emitted items provided by \"valuePublisher\"\n     *\/\n    private final Function&lt;IVALUE, OVALUE&gt; valueExtractor;\n\n    private CacheMono(Function&lt;KEY, Mono&lt;OVALUE&gt;&gt; valueSupplier, Flux&lt;IVALUE&gt; valuePublisher,\n            Function&lt;IVALUE, KEY&gt; keyExtractor, Function&lt;IVALUE, OVALUE&gt; valueExtractor) {\n        this.valueSupplier = valueSupplier;\n        this.valuePublisher = valuePublisher;\n        this.keyExtractor = keyExtractor;\n        this.valueExtractor = valueExtractor;\n    }\n\n    \/**\n     * Factory method to create a CacheMono instance from an external value supplier. The value\n     * supplier is called by this CacheMono instance for retrieving values when they are missing\n     * in cache (\"pull\" principle to retrieve not yet cached values).\n     *\/\n    public static &lt;KEY, VALUE&gt; CacheMono&lt;KEY, VALUE, VALUE&gt; fromSupplier(\n            @NonNull Function&lt;KEY, Mono&lt;VALUE&gt;&gt; valueSupplier) {\n        Objects.requireNonNull(valueSupplier);\n        return new CacheMono&lt;&gt;(valueSupplier, null, null, null);\n    }\n\n    \/**\n     * Factory method to create a CacheMono instance from an external value publisher.\n     * Published values will fill this cache (reactive \"push\" way).\n     *\/\n    public static &lt;KEY, VALUE&gt; CacheMono&lt;KEY, VALUE, VALUE&gt; fromPublisher(\n            @NonNull Flux&lt;VALUE&gt; valuePublisher, @NonNull Function&lt;VALUE, KEY&gt; keyExtractor) {\n        Objects.requireNonNull(valuePublisher);\n        Objects.requireNonNull(keyExtractor);\n        return createCacheMono(valuePublisher, keyExtractor, Function.identity());\n    }\n\n    \/**\n     * Factory method to create a CacheMono instance from an external value publisher.\n     * Published values will fill this cache (reactive \"push\" way).\n     *\/\n    public static &lt;KEY, IVALUE, OVALUE&gt; CacheMono&lt;KEY, IVALUE, OVALUE&gt; fromPublisher(\n            @NonNull Flux&lt;IVALUE&gt; valuePublisher,\n            @NonNull Function&lt;IVALUE, KEY&gt; keyExtractor,\n            @NonNull Function&lt;IVALUE, OVALUE&gt; valueExtractor) {\n        Objects.requireNonNull(valuePublisher);\n        Objects.requireNonNull(keyExtractor);\n        return createCacheMono(valuePublisher, keyExtractor, valueExtractor);\n    }\n  \n    private static &lt;KEY, IVALUE, OVALUE&gt; CacheMono&lt;KEY, IVALUE, OVALUE&gt; createCacheMono(\n            @NonNull Flux&lt;IVALUE&gt; valuePublisher,\n            @NonNull Function&lt;IVALUE, KEY&gt; keyExtractor,\n            @NonNull Function&lt;IVALUE, OVALUE&gt; valueExtractor) {\n        var cacheMono = new CacheMono&lt;&gt;(null, valuePublisher, keyExtractor, valueExtractor);\n        valuePublisher.doOnEach(signal -&gt; {\n            if (signal.hasValue()) {\n                final var inputValue = signal.get();\n                final var outputSignal = Signal.next(valueExtractor.apply(inputValue));\n                cacheMono.cache.put(keyExtractor.apply(inputValue),\n                                    new CacheMonoValue&lt;&gt;(outputSignal));\n            } else if (signal.isOnError()) {\n                if (signal.getThrowable() == null) {\n                    log.error(\"Error from value publisher\");\n                } else {\n                    log.error(\"Error from value publisher, message = {}\",\n                              signal.getThrowable().getMessage());\n                }\n            }\n        }).subscribe();\n\n        return cacheMono;\n    }\n    \n    ...\n}\n<\/pre>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">Not yet cached values will be retrieved either by <code>valueSupplier<\/code> or <code>valuePublisher<\/code>. The first one uses the \u201cpull\u201d principle and the second one uses the \u201cpush\u201d principle to retrieve not yet cached values. That means, either <code>valueSupplier<\/code> or <code>valuePublisher<\/code> along with <code>keyExtractor<\/code> and <code>valueExtractor<\/code> should be set.<div style=\"display:inline-block; margin: 15px 0;\"> <div id=\"adngin-JavaCodeGeeks_incontent_video-0\" style=\"display:inline-block;\"><\/div> <\/div><\/p>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">Keep in mind: if you create more than one <code>CacheMono<\/code> from the same value publisher, you should pass in a <code>Flux<\/code> stream which caches the history and emits cached items from the beginning to future subscribers. This is necessary because this <code>CacheMono<\/code> implementation subscribes to the passed in Flux stream in order to fill cache automatically once the source Flux stream publishes values (reactive \u201cpush\u201d way vs. \u201cpull\u201d provided by another factory method). The simplest way to create a such <code>Flux<\/code> stream from existing one would be invoking of <code>cache()<\/code> method on any <code>Flux<\/code> stream.<\/p>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">As you could see, we cache instances of <code>CacheMonoValue<\/code>. This is just a wrapper around <code>Mono<\/code> or <code>Signal<\/code>. We can implement this class as an inner class.<\/p>\n<pre class=\"brush:java\">\nprivate static class CacheMonoValue&lt;VALUE&gt; {\n\n    private Mono&lt;VALUE&gt; mono;\n    private Signal&lt;VALUE&gt; signal;\n\n    CacheMonoValue(Mono&lt;VALUE&gt; mono) {\n        this.mono = mono;\n    }\n\n    CacheMonoValue(Signal&lt;VALUE&gt; signal) {\n        this.signal = signal;\n    }\n\n    Mono&lt;VALUE&gt; toMono() {\n        if (mono != null) {\n            return mono;\n        }\n        return Mono.justOrEmpty(signal).dematerialize();\n    }\n\n    Optional&lt;VALUE&gt; getValue() {\n        if (signal == null) {\n            return Optional.empty();\n        }\n        return Optional.ofNullable(signal.get());\n    }\n}\n<\/pre>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">We will see in few words, that a <code>Mono<\/code> value from a long-running operation is cached immediately. The same <code>Mono<\/code> instance is retrieved for all subsequent lookups with the same key. Once the result of <code>Mono<\/code> is available, the real value is cached as <code>Signal<\/code> under the same key. Well, step by step. Look at the <code>lookup<\/code> method first. It uses a well-known pattern: if value is missed in the cache, the logic within the <code>switchIfEmpty<\/code> operator gets executed.<\/p>\n<pre class=\"brush:java\">\n\/**\n * Finds a value by key in an in-memory cache or load it from a remote source.\n * The loaded value will be cached.\n *\/\npublic Mono&lt;OVALUE&gt; lookup(KEY key) {\n    return Mono.defer(() -&gt; getValueAsMono(key)\n            .switchIfEmpty(Mono.defer(() -&gt; onCacheMissResume(key)))\n    );\n}\n\nprivate Mono&lt;OVALUE&gt; getValueAsMono(KEY key) {\n    final Lock readLock = lock.readLock();\n    readLock.lock();\n    try {\n        return Mono.justOrEmpty(cache.get(key)).flatMap(CacheMonoValue::toMono);\n    } finally {\n        readLock.unlock();\n    }\n}\n\nprivate Mono&lt;OVALUE&gt; onCacheMissResume(KEY key) {\n    final Lock writeLock = lock.writeLock();\n    writeLock.lock();\n    try {\n        \/\/ check if value was already cached by another thread\n        final var cachedValue = cache.get(key);\n        if (cachedValue == null) {\n            final Mono&lt;OVALUE&gt; monoValue;\n            if (valuePublisher != null) {\n                \/\/ get value from external value publisher\n                monoValue = valuePublisher\n                        .filter(value -&gt; Objects.equals(keyExtractor.apply(value), key))\n                        .map(valueExtractor)\n                        .next();\n            } else if (valueSupplier != null) {\n                \/\/ get value from external supplier\n                monoValue = valueSupplier.apply(key);\n            } else {\n                throw new IllegalStateException(\"Value can be not determined,\" +\n                        \"neither valuePublisher nor valueSupplier were set\");\n            }\n            \/\/ cache Mono as value immediately\n            cache.put(key, new CacheMonoValue&lt;&gt;(monoValue));\n\n            \/\/ cache success and error values encapsulated in signal when it is available\n            return monoValue.doOnEach(signal -&gt; {\n                if (signal.isOnNext()) {\n                    cache.put(key, new CacheMonoValue&lt;&gt;(\n                      Signal.next(Objects.requireNonNull(signal.get())))\n                    );\n                } else if (signal.isOnError()) {\n                    final Signal&lt;OVALUE&gt; errorSignal;\n                    if (signal.getThrowable() == null) {\n                        errorSignal = Signal.error(\n                          new Throwable(\"Getting value from external provider failed\"));\n                    } else {\n                        errorSignal = Signal.error(signal.getThrowable());\n                    }\n                    cache.put(key, new CacheMonoValue&lt;&gt;(errorSignal));\n                }\n            });\n        }\n        return Mono.justOrEmpty(cachedValue).flatMap(CacheMonoValue::toMono);\n    } finally {\n        writeLock.unlock();\n    }\n}\n<\/pre>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">In the <code>onCacheMissResume<\/code>, a missed value will be retieved by the mentioned above <code>valueSupplier<\/code> or <code>valuePublisher<\/code>. As I said, the value is cached immediately as a <code>Mono<\/code> object and is returned for all subsequent lookups. As soon as the value from the long-running operation is available, the logic within <code>monoValue.doOnEach(...)<\/code> is executed. The value is encapsulated in <code>Signal<\/code> and can be returned by invoking<code>signal.get()<\/code>.<\/p>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">Let\u2019s implement some convenient methods as well. Especially methods which return already existing (cached) values from the cache.<\/p>\n<pre class=\"brush:java\">\n\/**\n * Gets cached values as Java Stream. Returned stream is not sorted.\n *\/\npublic Stream&lt;OVALUE&gt; getValues() {\n    final Lock readLock = lock.readLock();\n    readLock.lock();\n    try {\n        return cache.values().stream().flatMap(cachedValue -&gt; cachedValue.getValue().stream());\n    } finally {\n        readLock.unlock();\n    }\n}\n\n\/**\n * Gets cached value as Java Optional.\n *\/\npublic Optional&lt;OVALUE&gt; getValue(KEY key) {\n    final Lock readLock = lock.readLock();\n    readLock.lock();\n    try {\n        return Optional.ofNullable(cache.get(key)).flatMap(CacheMonoValue::getValue);\n    } finally {\n        readLock.unlock();\n    }\n}\n\n\/**\n * Removes the mapping for a key from this map if it is present.\n *\/\npublic void remove(KEY key) {\n    final Lock writeLock = lock.writeLock();\n    writeLock.lock();\n    try {\n        cache.remove(key);\n    } finally {\n        writeLock.unlock();\n    }\n}\n<\/pre>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">The usage of <code>CacheMono<\/code> class is simple. Just two code snippets from my current project. The first one creates a <code>CacheMono<\/code> instance by calling <code>CacheMono.fromSupplier<\/code>.<\/p>\n<pre class=\"brush:java\">\n@Service\n@Slf4j\n@RequiredArgsConstructor\npublic class TopologyRepository {\n\n    private final CacheMono&lt;TopologyRef, TopologyDto, TopologyDto> cache;\n    private final TopologyLoader topologyLoader;\n    private final TopologyCreator topologyCreator;\n\n    @Autowired\n    public UnoTopologyRepository(TopologyLoader topologyLoader,\n                                 TopologyCreator topologyCreator) {\n        this.topologyLoader = topologyLoader;\n        this.topologyCreator = topologyCreator;\n        cache = CacheMono.fromSupplier(this::retrieveTopology);\n    }\n\n    \/**\n     * Finds a topology from this repository by reference.\n     *\/\n    public Mono&lt;TopologyDto> findUnoTopology(TopologyRef topologyRef) {\n        return cache.lookup(topologyRef)\n                .doOnNext(topology ->\n                          log.info(\"Topology was found by lookup with key {}\", topologyRef))\n                .onErrorResume(err -> {\n                    log.error(\"Error on lookup Topology by key {}, message: {}\",\n                              topologyRef, err.getMessage());\n                    return Mono.empty();\n                });\n    }\n\n    private Mono&lt;TopologyDto> retrieveTopology(TopologyRef topologyRef) {\n        CompletableFuture&lt;UnoTopologyDto> future = CompletableFuture.supplyAsync(() -> {\n            final var loaderContext = topologyLoader.retrieveTopology(topologyRef);\n            return topologyCreator.createTopology(loaderContext);\n        });\n        return Mono.fromFuture(future);\n    }\n}\n<\/pre>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">The second one creates a <code>CacheMono<\/code> instance by calling <code>CacheMono.fromPublisher<\/code>.<\/p>\n<pre class=\"brush:java\">\n@Service\n@Slf4j\n@RequiredArgsConstructor\npublic class SspDefinitionenStore implements SspDefinitionConsumer {\n\n    private CacheMono&gt;VersionedId, SspDefinition, SspDefinition&gt; sspDefinitionCache;\n    private FluxSink&gt;SspDefinition&gt; sspDefinitionSink;\n\n    @PostConstruct\n    public void initialize() {\n        sspDefinitionCache = CacheMono.fromPublisher(\n                Flux.create(sink -&gt; sspDefinitionSink = sink),\n                SspDefinition::getId);\n    }\n\n    @Override\n    public void accept(SspDefinition sspDefinition) {\n        sspDefinitionSink.next(sspDefinition);\n    }\n\n    public Mono&gt;SspDefinition&gt; lookupSspDefinition(VersionedId sspId) {\n        return sspDefinitionCache.lookup(sspId)\n                .doOnNext(sspTopology -&gt; log.info(\n                    \"SspDefinition was found by lookup with key {}\", sspId))\n                .onErrorResume(err -&gt; {\n                    log.error(\"Error on lookup SspDefinition by key {}, message: {}\",\n                              sspId, err.getMessage());\n                    return Mono.empty();\n                });\n    }\n\n    public Optional&gt;SspDefinition&gt; findSspDefinition(VersionedId sspId) {\n        return sspDefinitionCache.getValue(sspId);\n    }\n\n    public Flux&gt;SspDefinition&gt; findSspDefinitions() {\n        return Flux.fromStream(sspDefinitionCache.getValues().filter(Objects::nonNull));\n    }\n\n    ...\n}<\/pre>\n<p class=\"hs ht fo hu b hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in io ip dn gk\">That\u2019s all. Have fun!<\/p>\n<div class=\"attribution\">\n<table>\n<tbody>\n<tr>\n<td>\n<p>Published on Java Code Geeks with permission by Oleg Varaksin, partner at our <a href=\"\/\/www.javacodegeeks.com\/join-us\/jcg\/\" target=\"_blank\" rel=\"noopener\">JCG program<\/a>. See the original article here: <a href=\"https:\/\/olegvaraksin.medium.com\/clever-cache-for-reactors-mono-objects-d18a5451414b\" target=\"_blank\" rel=\"noopener\">Clever cache for Reactor\u2019s Mono objects<\/a><\/p>\n<p>Opinions expressed by Java Code Geeks contributors are their own.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Data caching is a widespread technique in the programming. It allows to quickly retrieve data without making long-running operations. But there is a problem with caching of data retrieved as result of some long-running operation. If a cache value is missed, it will be requested. If it is requested by a long-running HTTP request or &hellip;<\/p>\n","protected":false},"author":200,"featured_media":112,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[],"class_list":["post-110989","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-java"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Clever cache for Reactor\u2019s Mono objects - Java Code Geeks<\/title>\n<meta name=\"description\" content=\"Interested to learn about Reactor? Check our article explaining how to Clever cache for Reactor\u2019s Mono objects.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Clever cache for Reactor\u2019s Mono objects - Java Code Geeks\" \/>\n<meta property=\"og:description\" content=\"Interested to learn about Reactor? Check our article explaining how to Clever cache for Reactor\u2019s Mono objects.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html\" \/>\n<meta property=\"og:site_name\" content=\"Java Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/javacodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2021-08-08T12:15:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Oleg Varaksin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Oleg Varaksin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html\"},\"author\":{\"name\":\"Oleg Varaksin\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/ac096549ff51a73f2e15f128920ed7e0\"},\"headline\":\"Clever cache for Reactor\u2019s Mono objects\",\"datePublished\":\"2021-08-08T12:15:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html\"},\"wordCount\":605,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"articleSection\":[\"Enterprise Java\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html\",\"name\":\"Clever cache for Reactor\u2019s Mono objects - Java Code Geeks\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"datePublished\":\"2021-08-08T12:15:00+00:00\",\"description\":\"Interested to learn about Reactor? Check our article explaining how to Clever cache for Reactor\u2019s Mono objects.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html#primaryimage\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"width\":150,\"height\":150,\"caption\":\"java-interview-questions-answers\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2021\\\/08\\\/clever-cache-for-reactors-mono-objects.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Enterprise Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\\\/enterprise-java\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Clever cache for Reactor\u2019s Mono objects\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"name\":\"Java Code Geeks\",\"description\":\"Java Developers Resource Center\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"alternateName\":\"JCG\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.javacodegeeks.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/javacodegeeks\",\"https:\\\/\\\/x.com\\\/javacodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/ac096549ff51a73f2e15f128920ed7e0\",\"name\":\"Oleg Varaksin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/fda1ce47105421f7a352a13dbefec14ab59d59ffae99c6c3002e5841578979d3?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/fda1ce47105421f7a352a13dbefec14ab59d59ffae99c6c3002e5841578979d3?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/fda1ce47105421f7a352a13dbefec14ab59d59ffae99c6c3002e5841578979d3?s=96&d=mm&r=g\",\"caption\":\"Oleg Varaksin\"},\"sameAs\":[\"http:\\\/\\\/ovaraksin.blogspot.com\\\/\"],\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/author\\\/Oleg-Varaksin\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Clever cache for Reactor\u2019s Mono objects - Java Code Geeks","description":"Interested to learn about Reactor? Check our article explaining how to Clever cache for Reactor\u2019s Mono objects.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html","og_locale":"en_US","og_type":"article","og_title":"Clever cache for Reactor\u2019s Mono objects - Java Code Geeks","og_description":"Interested to learn about Reactor? Check our article explaining how to Clever cache for Reactor\u2019s Mono objects.","og_url":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html","og_site_name":"Java Code Geeks","article_publisher":"https:\/\/www.facebook.com\/javacodegeeks","article_published_time":"2021-08-08T12:15:00+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","type":"image\/jpeg"}],"author":"Oleg Varaksin","twitter_card":"summary_large_image","twitter_creator":"@javacodegeeks","twitter_site":"@javacodegeeks","twitter_misc":{"Written by":"Oleg Varaksin","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html#article","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html"},"author":{"name":"Oleg Varaksin","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/ac096549ff51a73f2e15f128920ed7e0"},"headline":"Clever cache for Reactor\u2019s Mono objects","datePublished":"2021-08-08T12:15:00+00:00","mainEntityOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html"},"wordCount":605,"commentCount":0,"publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","articleSection":["Enterprise Java"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html","url":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html","name":"Clever cache for Reactor\u2019s Mono objects - Java Code Geeks","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html#primaryimage"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","datePublished":"2021-08-08T12:15:00+00:00","description":"Interested to learn about Reactor? Check our article explaining how to Clever cache for Reactor\u2019s Mono objects.","breadcrumb":{"@id":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html#primaryimage","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","width":150,"height":150,"caption":"java-interview-questions-answers"},{"@type":"BreadcrumbList","@id":"https:\/\/www.javacodegeeks.com\/2021\/08\/clever-cache-for-reactors-mono-objects.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.javacodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Java","item":"https:\/\/www.javacodegeeks.com\/category\/java"},{"@type":"ListItem","position":3,"name":"Enterprise Java","item":"https:\/\/www.javacodegeeks.com\/category\/java\/enterprise-java"},{"@type":"ListItem","position":4,"name":"Clever cache for Reactor\u2019s Mono objects"}]},{"@type":"WebSite","@id":"https:\/\/www.javacodegeeks.com\/#website","url":"https:\/\/www.javacodegeeks.com\/","name":"Java Code Geeks","description":"Java Developers Resource Center","publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"alternateName":"JCG","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.javacodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.javacodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.javacodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/javacodegeeks","https:\/\/x.com\/javacodegeeks"]},{"@type":"Person","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/ac096549ff51a73f2e15f128920ed7e0","name":"Oleg Varaksin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/fda1ce47105421f7a352a13dbefec14ab59d59ffae99c6c3002e5841578979d3?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/fda1ce47105421f7a352a13dbefec14ab59d59ffae99c6c3002e5841578979d3?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/fda1ce47105421f7a352a13dbefec14ab59d59ffae99c6c3002e5841578979d3?s=96&d=mm&r=g","caption":"Oleg Varaksin"},"sameAs":["http:\/\/ovaraksin.blogspot.com\/"],"url":"https:\/\/www.javacodegeeks.com\/author\/Oleg-Varaksin"}]}},"_links":{"self":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/110989","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/users\/200"}],"replies":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/comments?post=110989"}],"version-history":[{"count":0,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/110989\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media\/112"}],"wp:attachment":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media?parent=110989"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/categories?post=110989"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/tags?post=110989"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}