[[geode-samples-caching-inline]]
= Asynchronous Inline Caching with Spring
:apache-geode-name: Apache Geode
:apache-geode-version: {apache-geode-doc-version}
:apache-geode-docs: https://geode.apache.org/docs/guide/{apache-geode-version}
:apache-geode-javadoc: https://geode.apache.org/releases/latest/javadoc
:apache-geode-website: https://geode.apache.org/
:images-dir: ./images
:spring-boot-docs: https://docs.spring.io/spring-boot/docs/current/reference/html
:spring-boot-javadoc: https://docs.spring.io/spring-boot/docs/current/api
:spring-data-geode-docs: https://docs.spring.io/spring-data/geode/docs/current/reference/html
:spring-data-geode-javadoc: https://docs.spring.io/spring-data/geode/docs/current/api
:spring-data-website: https://spring.io/projects/spring-data
:spring-framework-docs: https://docs.spring.io/spring/docs/current/spring-framework-reference
:spring-framework-javadoc: https://docs.spring.io/spring/docs/current/javadoc-api
:toc: left
:toclevels: 2
:stylesdir: ../
:highlightjsdir: ../js/highlight
:docinfodir: guides


This guide walks you through building a simple Spring Boot application
using {spring-framework-docs}/integration.html#cache[Spring's Cache Abstraction]
backed by {apache-geode-website}[{apache-geode-name}] as the caching provider for Asynchronous Inline Caching.

It is assumed that the reader is familiar with the Spring _programming model_. No prior knowledge of Spring's
_Cache Abstraction_ or {apache-geode-name} is required to utilize caching in your Spring Boot applications.

Additionally, this Sample builds on the concepts from the link:caching-inline.html[Inline Caching with Spring]
and link:caching-look-aside.html[Look-Aside Caching with Spring] guides. Therefore, it would be helpful to have read
those guides before proceeding through this guide.

Let's begin.

TIP: Refer to the link:../index.html#geode-caching-provider-inline-caching[Inline Caching] section, and specifically,
link:../index.html#geode-caching-provider-inline-caching-asynchronous[Asynchronous Inline Caching],
in the link:../index.html#geode-caching-provider[Caching with {apache-geode-name}] chapter
of the reference documentation for more information.

[#index-link]
link:../index.html[Index]

link:../index.html#geode-samples[Back to Samples]

[[geode-samples-caching-inline-asynchronous-background]]
== Background

In _Synchronous Inline Caching_, data is immediately read from or written to the primary data source, (a.k.a. the
_System of Record_ (SOR)), before the cache is modified, thereby guaranteeing a degree of consistency between the cache
and the backend data source. The "synchronous" arrangement of the _Inline Caching_ pattern is commonly referred to as
"_Read/Write-Through_".

With _Asynchronous Inline Caching_, data changes are written to the primary data source asynchronously, after the cache
has already been modified. The "asynchronous" arrangement of the _Inline Caching_ pattern is commonly referred to as
"_Write-Behind_". The cache entry is modified, then, and only then, will the primary data source reflect the changes
sometime later.

Due to the asynchronous nature of _Asynchronous Inline Caching_, it is possible for the primary data source (i.e.
_System of Record_ (SOR)) and cache to be out-of-sync. Additionally, the primary data source may contain information
that the cache does not. That is, another application may be updating the primary data source and not using the cache.
Conversely, a cache entry change may not be promptly written to the primary data source until the "_Write-Behind_"
operation is triggered, which is often implementation dependent. A data change could violate a database constraint,
fail to commit and be rolled back. All sorts of reasons can cause the primary data source and the cache to get
out-of-sync, or become inconsistent.

For this reason, throughput and latency are the primary application concerns and motivation, rather than consistency,
when using the _Asynchronous Inline Caching_ pattern.

The general pattern of _Inline Caching_ is depicted as follows:

image::{images-dir}/Inline-Caching-Overview.png[]

The layer in the application/system architecture involving the _Inline Caching_ logic sits between the cache
and the primary data source:

image::{images-dir}/Inline-Caching-Layer.png[]

In _Synchronous, Read/Write-Through, Inline Caching_, the system/application architecture appears as follows:

image::{images-dir}/Synchronous-Inline-Caching.png[]

With _Asynchronous, Write-Behind, Inline Caching_, the system/application architecture would instead appear as:

image::{images-dir}/Asynchronous-Inline-Caching.png[]

IMPLEMENTATION

As readers should know or will learn, the application cache is backed by an {apache-geode-name} Region.

In _Synchronous_, _Read-Through_ and/or _Write-Through_, _Inline Caching_, a `CacheLoader` is configured for the Region
and used to "_Read-Through_" to the backend/primary data source on a cache miss. When a cache entry is written, a
configured `CacheWriter` for the Region is invoked to "_Write-Through_" to the backend/primary data source. The cache
is only modified if the `CacheWriter` was successful in modifying the backend/primary data source.

Both the `CacheLoader` and `CacheWriter` are optional. That is, you can configure just one side of
_Synchronous Inline Caching_ or the other, either "_Read-Through_" or "_Write-Through_", both, or neither.

With _Asynchronous, Write-Behind, Inline Caching_, you (may) configure the Region with an associated `AsyncEventQueue`
(AEQ) and registered `AsyncEventListener`. When the cache is written to, the entry event is then forwarded and stored
on the AEQ, where at some time later, the registered `AsyncEventListener` for the AEQ will be invoked to process the
(batch of) `AsyncEvents`, which can then asynchronously modify the backend/primary data source.

Unlike _Synchronous Inline Caching_, _Asynchronous Inline Caching_ does not have an equivalent for "_Read-Through_",
such as "_Read-Behind_", particularly in a Reactive sense.

NOTE: At some point later, we may consider the development of "_Read-Behind_" with with use of Reactive Programming
and the Reactive Spring Data Repository abstraction.

TIP: It should also be intuitive that the listener registered on the AEQ attached to the (cache) Region does not have to
process the events by writing to a backend data store. It could write to a message queue, to the file system, or do
just about anything a user desires. However, OOTB SBDB provides support to inject a Spring Data Repository into an
AEQ listener to write to any backend data store supported by the Spring Data Repository abstraction.

link:../index.html#geode-samples[Back to Samples]

[[geode-samples-caching-inline-asynchronous-example]]
== Example

For our example, we have built a Golf Tournament application that runs a simulation with a set of professional golfers
playing at _The Masters_.  The (12) golfers play 18 holes of golf in pairs and proceed from hole 1 to hole 18 in under
a minute. For each hole played, their score of the hole is calculated. At the end of the round, each golfer's final
score is calculated relative to par for the golf course (72).

The Golf Tournament application is a Spring Boot application using {apache-geode-name} to cache the golfers score in
realtime as the players complete each hole.  However, to make the play "official", the golfer's score is recorded to a
backend database (RDBMS), asynchronously using _Asynchronous_, _Write-Behind_, _Inline Caching_. It is assumed that
there is additional validation required (e.g. signing scorecards) that goes on before the final score is accepted
and recorded to the _System of Record_ (SOR), in the "history books", so to speak.

Now that the problem context has been established, let's review a few of the application classes.

NOTE: Each of the application domain classes are code snippets or simply a preview of the actual class, and not actual
code. See the actual Sample {github-samples-url}/caching/inline-async[code] for more detail.

We start by defining our Golf Tournament application domain model types, starting with the `Golfer` class. Essentially,
the `Golfer` class models a person who plays golf and is defined as:

.`Golfer` class.
[source,java]
----
@Entity
@Table(name = "golfers")
public class Golfer implements Comparable<Golfer> {

    @javax.persistence.Id @Id
    private String name;

    private Integer hole = 0;

    private Integer score = 0;

}
----

The `Golfer` class has been annotated with JPA's `@Entity` annotation making it a proper (persistent) entity class.
The `Golfer` class is also annotated with `@Table` to persist instances of `Golfer` into the "_golfers_" table
of the database.

The application also defines a non-entity, `GolfCourse` class to model the golf course, which requires a name
and `List` of pars for each hole (all 18 holes) of the golf course:

.`GolfCourse` class
[source,java]
----
class GolfCourse {

    private final String name;

    private final List<Integer> parForHole = new ArrayList<>(18);

}
----

Next, a non-entity, `GolfTournament` class has been defined to model the golf tournament being played. It expects a name
for the tournament, the `GolfCourse` where the tournament is held and played, and a `Set` of `Golfers` (players)
registered to play.

Additionally, the `GolfTournament` class contains an inner class, the `Pairing` class, to group the registered players
into pairs to play a round.

.`GolfTournament` class
[source,java]
----
class GolfTournament implements Iterable<Pairing> {

    private final String name;

    private GolfCourse golfCourse;

    private final List<Pairing>  pairings = new ArrayList<>();

    private final Set<Golfer> players = new ArrayList<>();

    public static class Pair {

        private final Golfer playerOne;
        private final Golfer playerTwo;

    }
}
----

The `GolfTournament.Pairing` class serves as a composite acting on both players in the pair, such as to advance
the hole of play.

The `GolfTournament` class has additional builder methods to register players, build pairings, enable the tournament
to be played and determine when the tournament is finished (i.e. when all pairs complete all 18 holes of play).

There is a `GolferRepository` interface extending the `JpaRepository` interface to persist the state of each `Golfer`
to the backend database:

.`GolferRepository` interface
[source,java]
----
interface GolferRepository extends JpaRepository<Golfer, String> { }
----

NOTE: While `GolferRepository` extends from the `JpaRepository` interface directly, it is recommended to extend
the `CrudRepository` interface instead, keeping your application SD _Repositories_ agnostic from the underlying
data store. The reason `GolferRepository` extends from the `JpaRepository` interface directly, is to make it absolutely
clear that the `Golfer` state will be persisted to a backend database (RDBMS) with JPA using Hibernate as the provider.

The `GolferRepository` will be used by SBDG's _Asynchronous Inline Caching_ framework and infrastructure components.

The _Repository_ is injected into and used by the `AsyncEventListener` registered on the AEQ attached to the "Golfers"
Region to perform _Asynchronous_, _Write-Behind_, _Inline Caching_, operations to the backend database
and _System of Record_ (SOR).

We'll see in a moment how this association is made and how _Asynchronous Inline Caching_ is setup, made simple by SBDG.

To encapsulate the application logic and provide a (possibly transactional) facade to the `Golfer's` state,
a `GolferService` class has been defined:

.`GolferService` class
[source,java]
----
@Service
class GolferService {

	@CachePut(cacheNames = "Golfers", key = "#golfer.name")
	public Golfer update(Golfer golfer) {
		return golfer;
	}

    public List<Golfer> getAllGolfersFromCache() {
        // Use SDG GemfireTemplate to access the "Golfers" Region
    }

    public List<Golfer> getAllGolfersFromDatabase() {
        // Use the GolfersRepository to access the "Golfers" stored in the database.
    }
}
----

The `GolferService` class has been marked as a application service using Spring's `@Service` stereotype annotation.

Along with the `GolferService` the application uses a `PgaTourService` class to manage and run a (single)
`GolfTournament`.  Its primary method used to run a `GolfTournament` is the `play()` method:

.`PgaTourService` class, `play()` method
[source,java]
----
include::{samples-dir}/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/PgaTourService.java[tags=play]
----

This is a Spring `@Scheduled` service method called every 2.5 seconds after an initial delay of 5 seconds. Essentially,
the service method iterates through the pairings and each `Golfer` plays all 18 holes. Their scores are calculated
and recorded for each hole until the round is completed, where the players score is then calculated relative to par
for the golf course and recorded to the cache, which eventually updates the database.

To get everything started, a Spring Boot application class (i.e. a class annotated with the `@SpringBootApplication`
annotation) is used to bootstrap the Golf Tournament application.

.`BootGeodeAsyncInlineCachingClientApplication` class
[source,java]
----
include::{samples-dir}/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java[tags=class]
----

The `GolfTournament` is kicked off in the `ApplicationRunner`.

.`ApplicationRunner` bean in the `GolfApplicationConfiguration` class
[source,java]
----
include::{samples-dir}/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java[tags=application-configuration]
----

As the golf tournament progresses (in the `@Scheduled`, `PgaTourService.play()` service method), updates to the `Golfers`
in the pairs are written to the "_Golfers_" cache (i.e. "_Golfers_" Region) by calling
the `GolferService.update(:Golfer)` service method:

.`GolferService` class, `update(:Golfer)` method
[source,java]
----
include::{samples-dir}/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/GolferService.java[tags=cache-put]
----

This service method simply "puts" the `Golfer` in the cache (i.e. "_Golfers_" Region) mapped to the `Golfer's name`
(as a key/value cache entry).

The cache/Region entry `put` operation results in cache event being added to the AEQ, which will eventually trigger
the SBDG framework-provided `AsyncEventListener` with our injected `GolferRepository` to write the `Golfer's` state
to the backend database.

The configuration of the "_Golfers_" Region (cache) with an AEQ and listener using the `GolferRepository` is defined
as follows:

.`AsyncInlineCachingConfiguration` class
[source,java]
----
include::{samples-dir}/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java[tags=class]
----

The Spring `@Configuration` class used to enable _Async Inline Caching_ consists of 2 different AEQ configurations
and bean definitions.

The first is a AEQ configured with a "preference" for being triggered on the *batch size*, i.e. the number of events
present in the AEQ:

.AEQ batch size configuration
[source,java]
----
include::{samples-dir}/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java[tags=queue-batch-size]
----

The second AEQ configuration uses a "preference" for being triggered based on a *batch time interval*, i.e. after
a period of time has elapsed, such as 5 seconds.

.AEQ batch time interval configuration
[source,java]
----
include::{samples-dir}/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java[tags=queue-batch-time-interval]
----

NOTE: The default AEQ _batch time interval_ in {apache-geode-name} is *5 milliseconds* (5 ms). However, to demonstrate
the asynchronous nature of the cache to database updates, a much longer delay was used. Likewise, the default AEQ
_batch size_ in {apache-geode-name} is *100*.

In both AEQ configurations and bean definitions, the _batch size_ and _batch time interval_ have been set (overriding
the {apache-geode-name} defaults) in order to show the effects of each AEQ configuration independently. As you can
imagine, particularly in a highly concurrent and transactional application with frequent updates, it would be hard to
determine whether the AEQ event processing (via the listener) was triggered by the _batch time interval_ or the
_batch size_. And, with a default *5 millisecond* _batch time interval_, it is hard to witness the asynchronous nature
of the cache to database updates to begin with.

We will have more to say on the AEQ configuration below, in the conclusion.

The final class in the golf application is a `GolferController` class annotated with Spring's `@RestController`
annotation in order to expose our golf application functionality as an API in a REST-ful interface:

.`GolferService` class, `update(:Golfer)` method
[source,java]
----
include::{samples-dir}/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolferController.java[tags=class]
----

The Spring Web MVC `@RestController` class exposes two REST-ful API web service endpoints returning JSON data:

* http://localhost:8080/api/golf/tournament/cache - used to get the current state of the `Golfers` from the cache
* http://localhost:8080/api/golf/tournament/database - used to get the current state of the `Golfers` from the database

Both web service endpoints are consumed by the `golf-tournament-view.html` page, which uses _jQuery_ and AJAX to make
periodic HTTP requests to refresh the page.

[[geode-samples-caching-inline-asynchronous-example-run]]
== Run the Example

To run the example, there are few more configuration details we need to cover.

While it is possible to run this example using an {apache-geode-name} client/server topology, we keep things simple
by running the example using a single Spring Boot application class, namely the
`BootGeodeAsyncInlineCachingClientApplication` along with a peer cache configuration.

That is, in our `BootGeodeAsyncInlineCachingClientApplication` class, we also apply the
`PeerCacheApplicationConfiguration` by enabling the Spring Profile, "_peer-cache_":

.`PeerCacheApplicationConfiguration` class
[source,java]
----
include::{samples-dir}/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java[tags=peer-cache-configuration]
----

It should be noted that AEQs can only be created and registered on Regions existing on the server-side of
an {apache-geode-name} system. That is, you cannot add an AEQ to a client-side Region. Therefore, in all your
_Async Inline Caching_ Uses Cases (UC), synchronous or asynchronous, it will be the servers in an {apache-geode-name}
cluster that are responsible for _Write-Behind_ functionality to the backend data store, not a Spring Boot,
{apache-geode-name} client application.

However, for demonstration purposes, we override SBDG's _auto-configuration_ providing a `ClientCache` instance
by default simply by enabling the "_peer-cache_" Spring Profile, which replaces the `ClientCache` instance with
a peer `Cache` instance instead.

Finally, when running this application, you must decide on your AEQ management strategy.

For example, do you want the AEQ listener to be triggered by *batch size* (i.e. the number of cache events) or using
the *batch time interval*. Each strategy can be enabled using a Spring Profile, either "_queue-batch-size_"
or "_queue-batch-time-interval_". This allows you to experiment with different AEQ management strategies and observe
the effects.

In total, the Spring Profiles you need to enable would appear as follows:

.Spring Profiles to enable when running the application.
[source,txt]
----
-Dspring.profiles.active=peer-cache,queue-batch-size,server
----

Of course, you can replace "_queue-batch-size_" with "_queue-batch-time-interval_".

The final run configuration of the Spring Boot application, as seen in IntelliJ IDEA is:

image::{images-dir}/BootGeodeAsyncInlineCachingClientApplication-IntelliJ-IDEA-Run-Configuration.png[]

To access the golf application, simply navigate to:

http://localhost:8080/golf-tournament-view.html

You should see a web page similar to:

image::{images-dir}/Asynchronous-Inline-Caching-Application.png[]

You can also run this example using the SBDG Gradle build from the command-line like so:

.Run the example using Gradle
[source,txt]
----
$ gradlew --no-daemon :spring-geode-sample-caching-inline-async:bootRun
----

This is convenient since the Spring Profiles are already configured for you.

However, when you switch to using the "_queue-batch-time-interval_" you will see a similar effect and behavior, but on
a slightly different schedule for the database updates, i.e. at a fixed 5 second interval.

[[geode-samples-caching-inline-asynchronous-example-conclusion]]
== Conclusion

_Asynchronous Inline Caching_ can be a powerful pattern of caching applied to your Spring Boot application workflows
depending on the use case and requirements.

If throughput and latency are absolutely critical to your application design in order to achieve the necessary
responsiveness and quality of experience your users' expect, and consistency (i.e. between the cache and the backend
_System of Record_ (SOR), or database) is not as important of a concern, then you might want to consider
_Asynchronous Inline Caching_.

There are many factors to consider in the configuration of the AEQ that is at the heart of the _Asynchronous Inline
Caching_ pattern, such as the appropriate *batch size* and *batch time interval*. Neither setting is exclusive
from the other, in fact. Both settings are considered when {apache-geode-name} makes a decision of when to trigger
the listener registered on the AEQ to process the events for operations originating from the Region to which the AEQ
is attached.

You must decide on the *batch size*, based on how many events might occur in a given period of time. If the frequency
is quite high, then you might need a smaller *batch size*, for instance. The AEQ is in-memory after all, therefore you
must be conscious of memory constraints on your system, especially during peak loads. Of course, the AEQ can be
configured to overflow events to disk and even persist events between restarts, but ideally you want these events
to be processed in as near realtime as possible.

However, when the load on your application is low and events occur sporadically, you must also be mindful that the
events do not sit in the AEQ for too long.  If you have *batch size* of 1000, and there are currently only 20 events
(well, any number of events less than 1000) sitting in the AEQ waiting to be processed, then the *batch time interval*
becomes important, especially so that these remaining events (less than the configured *batch size*) don't wait in the
queue indefinitely. The configured Queue Dispatcher Thread count plays into this as well.

Other factors to consider are whether you can conflate the events in the AEQ. This minimizes the number of events for
a single logical Object to the latest update. Additionally, do you need to overflow events to disk after the configured
maximum queue memory is reached, or should events simply be discarded? Do you need to maintain the events in the queue
between restarts (i.e. configure the AEQ to be persistent)? Do the disk writes for overflow and/or persistence need to
be synchronous? Do the events in the queue need to be ordered based on some `OrderPolicy`? Do the events need to be
filtered? How many dispatcher threads do you require? Etc. Etc.

There are many important things consider in the configuration of the AEQ when using _Asynchronous Inline Caching_
for _Write-Behind_ capabilities. Usually, it is safe to start with the defaults and adjust as needed, and as your
measurements and tests dictate.

We hope that you found this guide useful and informative when tackling difficult problems, the kind of application
problems where the _Asynchronous Inline Caching_ pattern can be applied with immediate benefits.

link:../index.html#geode-samples[Back to Samples]
