[[geode-caching-provider]]
== Caching with Apache Geode
:gemfire-name: {pivotal-gemfire-name}
:geode-name: {apache-geode-name}


One of the easiest, quickest and least invasive ways to get started using {geode-name} in your Spring Boot applications
is to use {geode-name} as a {spring-framework-docs}/integration.html#cache-store-configuration[_caching provider_]
in {spring-framework-docs}/integration.html#cache[Spring's Cache Abstraction].  SDG
{spring-framework-docs}/integration.html#cache-store-configuration-gemfire[enables]
{geode-name} to function as a _caching provider_ in Spring's Cache Abstraction.

TIP: See the _Spring Data for {geode-name} Reference Guide_ for more details on the
{spring-data-geode-docs-html}/#apis:spring-cache-abstraction[support] and {spring-data-geode-docs-html}/#bootstrap-annotation-config-caching[configuration]
of {geode-name} as a _caching provider_ in Spring's Cache Abstraction.

TIP: Make sure you thoroughly understand the {spring-framework-docs}/integration.html#cache-strategies[concepts]
behind Spring's Cache Abstraction before you continue.

TIP: You can also refer to the relevant section on {spring-boot-docs-html}/#boot-features-caching[Caching]
in _Spring Boot's Reference Documentation_. _Spring Boot_ even provides _auto-configuration_ support for a few,
simple {spring-boot-docs-html}/#_supported_cache_providers[caching providers] out-of-the-box.

Indeed, _caching_ can be a very effective _software design pattern_ to avoid the cost of invoking
a potentially expensive operation when, given the same input, the operation yields the same output every time.

Some classic examples of caching include, but are not limited to: looking up a customer by name or account number,
looking up a book by ISBN, geocoding a physical address, caching the calculation of a person's credit score
when the person applies for a financial loan.

If you need the proven power of an enterprise-class caching solution, with strong consistency, high availability,
low latency and multi-site (WAN) capabilities, then you should consider https://geode.apache.org/[{geode-name}],
or alternatively, VMWare, Inc. offers a commercial solution built on {geode-name} called, {gemfire-name}.

Spring's {spring-framework-docs}/integration.html#cache-annotations[declarative, annotation-based caching] makes it
extremely simple to get started with caching, which is as easy as annotating your application components with
the appropriate Spring cache annotations.

TIP: Spring's declarative, annotation-based caching also {spring-framework-docs}/integration.html#cache-jsr-107[supports]
JCache (JSR-107) annotations.

For example, suppose you want to cache the results of determining a person's eligibility when applying for a financial
loan.  A person's financial status is unlikely to change in the time that the computer runs the algorithms to compute
a person's eligibility after all the financial information for the person has been collected and submitted for review
and processing.

Our application might consist of a financial loan service to process a person's eligibility over a given period of time:

.Spring application service component applicable to caching
[source,java]
----
@Service
class FinancialLoanApplicationService {

    @Cacheable("EligibilityDecisions")
    EligibilityDecision processEligibility(Person person, Timespan timespan) {
        // ...
    }
}
----

Notice the `@Cacheable` annotation declared on the `processEligibility(:Person, :Timespan)` method of our service class.

When the `FinancialLoanApplicationService.processEligibility(..)` method is called, Spring's caching infrastructure
first consults the "_EligibilityDecisions_" cache to determine if a decision has already been computed for the given
person within the given span of time.  If the person's eligibility in the given time frame has already been determined,
then the existing decision is returned from the cache.  Otherwise, the `processEligibility(..)` method will be invoked
and the result of the method will be cached when the method returns, before returning the decision to the caller.

Spring Boot for {geode-name} _auto-configures_ {geode-name} as the _caching provider_ when {geode-name} is declared on
the application classpath, and when no other _caching provider_ (e.g. Redis) has been configured.

If Spring Boot for {geode-name} detects that another _cache provider_ has already been configured, then {geode-name}
will not function as the _caching provider_ for the application.  This allows users to configure another store, e.g.
Redis, as the _caching provider_ and perhaps use {geode-name} as your application's persistent store.

The only other requirement to enable caching in a Spring Boot application is for the declared caches (as specified
in Spring's or JSR-107's caching annotations) to have been created and already exist, especially before the operation
on which caching has been applied is invoked. This means the backend data store must provide the data structure
serving as the "_cache_".  For {geode-name} this means a cache `Region`.

To configure the necessary Regions backing the caches declared in Spring's cache annotations, this is as simple as
using Spring Data for {geode-name}'s
{spring-data-geode-javadoc}/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegions.html[`@EnableCachingDefinedRegions`]
annotation.

The complete Spring Boot application looks like this:

[source,java]
----
package example.app;

@SpringBootApplication
@EnableCachingDefinedRegions
class FinancialLoanApplication {

    public static void main(String[] args) {
        SpringApplication.run(FinancialLoanApplication.class, args);
    }
}
----

TIP: The `FinancialLoanApplicationService` is picked up by Spring's classpath component scan since this class
is annotated with Spring's `@Service` stereotype annotation.

TIP: You can set the `DataPolicy` of the Region created through the `@EnableCachingDefinedRegions` annotation by
setting the `clientRegionShortcut` to a valid enumerated value.

NOTE: Spring Boot for {geode-name} does not recognize nor apply the `spring.cache.cache-names` property. Instead, you
should use SDG's `@EnableCachingDefinedRegions` on an appropriate Spring Boot application `@Configuration` class.

[[geode-caching-provider-look-aside-near-inline-multi-site]]
=== Look-Aside Caching, Near Caching, Inline Caching and Multi-Site Caching

Four different types of caching patterns can be applied with Spring when using {geode-name} for your application caching
needs.

The 4 primary caching patterns include:

* _Look-Aside Caching_
* _Near Caching_
* _Inline Caching_
* _Multi-Site Caching_

Typically, when most users think of caching, they are thinking of _Look-Aside Caching_. This is the default caching
pattern applied by _Spring's Cache Abstraction_.

In a nutshell, _Near Caching_ keeps the data closer to where the data is used thereby improving on performance due to
lower latencies when data is needed (i.e. no extra network hops). This also improves application throughput, i.e. the
amount of work completed in a given period of time.

Within _Inline Caching_, developers have a choice between synchronous (_Read/Write-Through_) and asynchronous
(_Write-Behind_) configurations depending on the application use case and requirements. Synchronous, Read/Write-Through
_Inline Caching_ is necessary if consistency is a concern.  Asynchronous, Write-Behind _Inline Caching_ is applicable
if throughput and low-latency are a priority.

Within _Multi-Site Caching_, there are _Active-Passive_ and _Active-Active_ arrangements.  More details on _Multi-Site
Caching_ will be presented in a later release.

[[geode-caching-provider-look-aside-caching]]
==== Look-Aside Caching

TIP: Refer to the corresponding Sample link:guides/caching-look-aside.html[Guide] and {github-samples-url}/caching/look-aside[Code]
to see _Look-Aside Caching_ using {apache-geode-name} in action!

The caching pattern demonstrated in the example above is a form of
https://content.pivotal.io/blog/an-introduction-to-look-aside-vs-inline-caching-patterns[_Look-Aside Caching_].

Essentially, the data of interest is searched for in the cache first, before calling a potentially expensive
operation, e.g. like an operation that makes an IO or network bound request resulting in either a blocking,
or a latency sensitive computation.

If the data can be found in the cache (stored in-memory to reduce latency) then the data is returned without ever
invoking the expensive operation.  If the data cannot be found in the cache, then the operation must be invoked.
However, before returning, the result of the operation is cached for subsequent requests when the the same input
is requested again, by another caller resulting in much improved response times.

Again, typical _Look-Aside Caching_ pattern applied in your application code looks similar to the following:

.Look-Aside Caching Pattern Applied
[source,java]
----
@Service
class CustomerService {

  private final CustomerRepository customerRepository;

  @Cacheable("Customers")
  Customer findByAcccount(Account account) {

    // pre-processing logic here

    Customer customer = customerRepository.findByAccoundNumber(account.getNumber());

    // post-processing logic here

    return customer;
  }
}
----

In this design, the `CustomerRepository` is perhaps a JDBC or JPA/Hibernate backed implementation accessing
the external data source (i.e. RDBMS) directly.  The `@Cacheable` annotation wraps, or "decorates",
the `findByAccount(:Account):Customer` operation to provide caching facilities.

NOTE: This operation may be expensive because it might validate the Customer's Account before looking up the Customer,
pull multiple bits of information to retrieve the Customer record, and so on, hence the need for caching.

[[geode-caching-provider-near-caching]]
==== Near Caching

TIP: Refer to the corresponding Sample link:guides/caching-near.html[Guide] and {github-samples-url}/caching/near[Code]
to see _Near Caching_ using {apache-geode-name} in action!

_Near Caching_ is another pattern of caching where the cache is collocated with the application.  This is useful when
the caching technology is configured using a client/server arrangement.

We already mentioned that Spring Boot for {geode-name} <<clientcache-applications.adoc#geode-clientcache-applications, provides>>
an _auto-configured_, `ClientCache` instance, out-of-the-box, by default.  A `ClientCache` instance is most effective
when the data access operations, including cache access, is distributed to the servers in a cluster accessible by the
client, and in most cases, multiple clients.  This allows other cache client applications to access the same data.
However, this also means the application will incur a network hop penalty to evaluate the presence of the data
in the cache.

To help avoid the cost of this network hop in a client/server topology, a local cache can be established, which
maintains a subset of the data in the corresponding server-side cache (i.e. Region).  Therefore, the client cache
only contains the data of interests to the application.  This "local" cache (i.e. client-side Region) is consulted
before forwarding the lookup request to the server.

To enable _Near Caching_ when using either {geode-name}, simply change the Region's (i.e. the `Cache` in Spring's
Cache Abstraction) data management policy from `PROXY` (the default) to `CACHING_PROXY`, like so:

.Enabling Near Caching using {geode-name}
[source,java]
----
@SpringBootApplication
@EnableCachingDefinedRegions(clientRegionShortcut = ClientRegionShortcut.CACHING_PROXY)
class FinancialLoanApplication {

    public static void main(String[] args) {
        SpringApplication.run(FinancialLoanApplication.class, args);
    }
}
----

TIP: The default, client Region data management policy is
{apache-geode-javadoc}/org/apache/geode/cache/client/ClientRegionShortcut.html#PROXY[`ClientRegionShortcut.PROXY`].
As such, all data access operations are immediately forwarded to the server.

TIP: Also see the {geode-name} documentation concerning
{apache-geode-docs}/developing/events/how_client_server_distribution_works.html[Client/Server Event Distribution]
and specifically, "_Client Interest Registration on the Server_" when client CACHING_PROXY Regions to manage state
in addition to the corresponding server-side Region.  This is necessary to receive updates on entries in the Region
that might have been changed by other clients accessing the same data.

[[geode-caching-provider-inline-caching]]
==== Inline Caching

TIP: Refer to the corresponding Sample link:guides/caching-inline.html[Guide] and {github-samples-url}/caching/inline[Code]
to see _Inline Caching_ using {geode-name} in action!

The next pattern of caching we will discuss in this chapter is _Inline Caching_.

There are two different configurations of _Inline Caching_ that developers can apply to their Spring Boot applications
when using _Inline Caching_ pattern: Synchronous (_Read/Write-Through_) and Asynchronous (_Write-Behind_).

NOTE: Asynchronous (currently) only offers write capabilities, from the cache to the backend, external data source.
There is no option to asynchronously and automatically load the cache when the value becomes available in the backend,
external data source.

[[geode-caching-provider-inline-caching-synchronous]]
===== Synchronous Inline Caching

When employing _Inline Caching_ and a cache miss occurs, the application service method may still not be invoked since
a cache can be configured to invoke a loader to load the missing entry from an backend, external data source.

With {geode-name} the cache, or using {geode-name} terminology, the Region, can be configured with a
{apache-geode-javadoc}/org/apache/geode/cache/CacheLoader.html[CacheLoader]. A `CacheLoader` is implemented to retrieve
missing values from an external data source, which could be an RDBMS or any other type of data store (e.g. another NoSQL
data store like Apache Cassandra, MongoDB or Neo4j), when a cache miss occurs.

TIP: See the {geode-name} User Guide on
{apache-geode-docs}/developing/outside_data_sources/how_data_loaders_work.html[Data Loaders] for more details.

Likewise, an {geode-name} Region can also be configured with a
{apache-geode-javadoc}/org/apache/geode/cache/CacheWriter.html[CacheWriter].  A `CacheWriter` is responsible for writing
an entry put into the Region to the backend data store, such as an RDBMS. This is referred to as a "_write-through_"
operation because it is synchronous. If the backend data store fails to be updated then the entry will not be stored in
the Region. This helps to ensure consistency between the backend data store and the {geode-name} Region.

TIP: It is also possible to implement _Inline-Caching_ using _asynchronous_, _write-behind_ operations by registering
an {apache-geode-javadoc}/org/apache/geode/cache/asyncqueue/AsyncEventListener.html[AsyncEventListener] on an
{apache-geode-javadoc}/org/apache/geode/cache/asyncqueue/AsyncEventQueue.html[AEQ] attached to a server-side Region.
You should consult the {geode-name} User Guide for more
{apache-geode-docs}/developing/events/implementing_write_behind_event_handler.html[details]. We cover _asynchronous_,
_write-behind_ _Inline Caching_ in the next section.

The typical pattern of _Inline Caching_ when applied to application code looks similar to the following:

.Inline Caching Pattern Applied
[source,java]
----
@Service
class CustomerService {

  private CustomerRepository customerRepository;

  Customer findByAccount(Account account) {

      // pre-processing logic here

      Customer customer = customerRepository.findByAccountNumber(account.getNumber());

      // post-processing logic here.

      return customer;
  }
}
----

The main difference is, there are no Spring or JSR-107 caching annotations applied to the application's service methods
and the `CustomerRepository` is accessing {geode-name} directly and NOT the RDBMS.

[[geode-caching-provider-inline-caching-synchronous-cacheloader-cachewriter]]
====== Implementing CacheLoaders & CacheWriters for Inline Caching

You can use Spring to configure a `CacheLoader` or `CacheWriter` as a bean in the Spring `ApplicationContext`
and then wire the loader and/or writer to a Region. Given the `CacheLoader` or `CacheWriter` is a Spring bean
like any other bean in the Spring `ApplicationContext`, you can inject any `DataSource` you like into the Loader/Writer.

While you can configure client Regions with `CacheLoaders` and `CacheWriters`, it is typically more common to
configure the corresponding server-side Region; for example:

[source,java]
----
@SpringBootApplication
@CacheServerApplication
class FinancialLoanApplicationServer {

    public static void main(String[] args) {
        SpringApplication.run(FinancialLoanApplicationServer.class, args);
    }

	@Bean("EligibilityDecisions")
	PartitionedRegionFactoryBean<Object, Object> eligibilityDecisionsRegion(
            GemFireCache gemfireCache, CacheLoader decisionManagementSystemLoader,
            CacheWriter decisionManagementSystemWriter) {

        PartitionedRegionFactoryBean<?, EligibilityDecision> eligibilityDecisionsRegion =
            new PartitionedRegionFactoryBean<>();

        eligibilityDecisionsRegion.setCache(gemfireCache);
        eligibilityDecisionsRegion.setCacheLoader(decisionManagementSystemLoader);
        eligibilityDecisionsRegion.setCacheWriter(decisionManagementSystemWriter);
        eligibilityDecisionsRegion.setPersistent(false);

        return eligibilityDecisionsRegion;
    }


    @Bean
    CacheLoader<?, EligibilityDecision> decisionManagementSystemLoader(
            DataSource dataSource) {

        return new DecisionManagementSystemLoader(dataSource);
    }

    @Bean
    CacheWriter<?, EligibilityDecision> decisionManagementSystemWriter(
            DataSource dataSource) {

        return new DecisionManagementSystemWriter(dataSource);
    }

    @Bean
    DataSource dataSource() {
      // ...
    }
}
----

Then, you would implement the {apache-geode-javadoc}/org/apache/geode/cache/CacheLoader.html[`CacheLoader`]
and {apache-geode-javadoc}/org/apache/geode/cache/CacheWriter.html[`CacheWriter`] interfaces as appropriate:

.DecisionManagementSystemLoader
[source,java]
----
class DecisionManagementSystemLoader implements CacheLoader<?, EligibilityDecision> {

  private final DataSource dataSource;

  DecisionManagementSystemLoader(DataSource dataSource) {
    this.dataSource = dataSource;
  }

  public EligibilityDecision load(LoadHelper<?, EligibilityDecision> helper) {

     Object key = helper.getKey();

     // Use the configured DataSource to load the value identified by the key from a backend, external data store.
   }
}
----

TIP: SBDG provides the `org.springframework.geode.cache.support.CacheLoaderSupport` `@FunctionalInterface` to
conveniently implement application `CacheLoaders`.

If the configured `CacheLoader` still cannot resolve the value, then the cache lookup operation results in a miss
and the application service method will then be invoked to compute the value.

.DecisionManagementSystemWriter
[source,java]
----
class DecisionManagementSystemWriter implements CacheWriter<?, EligibilityDecision> {

  private final DataSource dataSource;

  DecisionManagementSystemWriter(DataSource dataSource) {
    this.dataSource = dataSource;
  }

  public void beforeCreate(EntryEvent<?, EligiblityDecision> entryEvent) {
    // Use configured DataSource to save (e.g. INSERT) the entry value into the backend data store
  }

  public void beforeUpdate(EntryEvent<?, EligiblityDecision> entryEvent) {
    // Use the configured DataSource to save (e.g. UPDATE or UPSERT) the entry value into the backend data store
  }

  public void beforeDestroy(EntryEvent<?, EligiblityDecision> entryEvent) {
    // Use the configured DataSource to delete (i.e. DELETE) the entry value from the backend data store
  }

  // ...
}
----

TIP: SBDG provides the `org.springframework.geode.cache.support.CacheWriterSupport` interface to
conveniently implement application `CacheWriters`.

NOTE: Of course, your `CacheWriter` implementation can use any data access technology to interface with
your backend data store (e.g. JDBC, Spring's `JdbcTemplate`, JPA/Hibernate, etc).  It is not limited to only using
a `javax.sql.DataSource`.  In fact, we will present another, more useful and convenient approach to implementing
_Inline Caching_ in the next section.

[[geode-caching-provider-inline-caching-synchronous-using-spring-data-repositories]]
====== Inline Caching using Spring Data Repositories

Spring Boot for {geode-name} (SBDG) offers dedicated support to configure _Inline Caching_ using Spring Data
Repositories.

This is very powerful because it allows you to:

1. Access any backend data store supported by Spring Data (e.g. Redis for Key/Value or other data structures,
MongoDB for Documents, Neo4j for Graphs, Elasticsearch for Search, and so on).

2. Use complex mapping strategies (e.g. ORM provided by JPA/Hibernate).

It is our belief that users should be storing data where it is most easily accessible. If you are accessing
and processing Documents, then MongoDB, Couchbase or another document store is probably going to be the most logical
choice to manage your application's Documents.

However, this does not mean you have to give up {geode-name} in your application/system architecture. You can leverage
each data store for what it is good at. While MongoDB is excellent at handling documents, {geode-name} is a highly
valuable choice for consistency, high availability, low-latency/high-throughput, multi-site, scale-out application
use cases.

As such, using {geode-name}'s `CacheLoader/CacheWriter` functionality provides a nice integration point between itself
and other data stores to best serve your application's use case and requirements.

EXAMPLE

Let's say you are using JPA/Hibernate to access (store and read) data managed in an Oracle Database. Then, you can
configure {geode-name} to read/write-through to the backend Oracle Database when performing cache (Region) operations
by delegating to a Spring Data JPA Repository.

The configuration might look something like:

.Inline Caching configuration using SBDG
[source,java]
----
@SpringBootApplication
@EntityScan(basePackageClasses = Customer.class)
@EnableEntityDefinedRegions(basePackageClasses = Customer.class)
@EnableJpaRepositories(basePackageClasses = CustomerRepository.class)
class SpringBootOracleDatabaseApacheGeodeApplication {

  @Bean
  InlineCachingRegionConfigurer<Customer, Long> inlineCachingForCustomersRegionConfigurer(
      CustomerRepository customerRepository) {

    return new InlineCachingRegionConfigurer<>(customerRepository, Predicate.isEqual("Customers"));
  }
}
----

Out-of-the-box, SBDG provides the `InlineCachingRegionConfigurer<ENTITY, ID>` interface.

Given a `Predicate` to express the criteria used to match the target Region by name and a Spring Data `CrudRepository`,
the `InlineCachingRegionConfigurer` will configure and adapt the Spring Data `CrudRepository` as a `CacheLoader` and
`CacheWriter` registered on the Region (e.g. "Customers") to enable _Inline Caching_ functionality.

You simply only need to declare `InlineCachingRegionConfigurer` as a bean in the Spring `ApplicationContext` and make
the association between the Region (by name) and the appropriate Spring Data `CrudRepository`.

In this example, we used JPA and Spring Data JPA to store/retrieve the data in the cache (Region) to/from a backend
database.  But, you can inject any Spring Data Repository for any data store (e.g. Redis, MongoDB, etc) that supports
the Spring Data Repository abstraction.

TIP: If you only want to support one way data access operations when using _Inline Caching_, then you can use either
the `RepositoryCacheLoaderRegionConfigurer` for reads or the `RepositoryCacheWriterRegionConfigurer` for writes,
instead of the `InlineCachingRegionConfigurer`, which supports both reads and writes.

TIP: To see a similar implementation of _Inline Caching_ using a Database (In-Memory, HSQLDB Database) in action, have a
look at this https://github.com/spring-projects/spring-boot-data-geode/blob/master/spring-geode/src/test/java/org/springframework/geode/cache/inline/database/InlineCachingWithDatabaseIntegrationTests.java[test class]
from the SBDG test suite. A dedicated sample will be provided in a future release.

[[geode-caching-provider-inline-caching-asynchronous]]
===== Asynchronous Inline Caching

If consistency between the cache and your external, backend data source is not a concern, and you only need to write
from the cache to the backend data store periodically, then you can employ asynchronous (_Write-Behind_) _Inline Caching_.

As the term "_Write-Behind_" implies, a write to the backend data store is asynchronous and not strictly tied to the
cache operation. As a result, the backend data store will be in an "_eventually consistent_" state since the cache is
primarily used by the application at runtime to access and manage data. In this case, the backend data store is used
to persist the state of the cache, and that of the application, at periodic intervals.

Of course, if multiple applications are updating the backend data store concurrently, you could combine a `CacheLoader`
to synchronously "_Read-Through_" to the backend data store and keep the cache up-to-date as well as asynchronously
_Write-Behind_ from the cache to the backend data store when the cache is updated to eventually inform other interested
applications of data changes. In this capacity, the backend data store is still the primary _System of Record_ (SOR).

If data processing is not time sensitive, you can gain a performance advantage from periodic, quantity and/or time-based
batch updates.

[[geode-caching-provider-inline-caching-asynchronous-asynceventlistener]]
====== Implementing an AsyncEventListener for Inline Caching

If you were to configure asynchronous (_Write-Behind_) _Inline Caching_ by hand, then you would need to do all of
the following yourself:

1. Implement an `AsyncEventListener` to write to an external, backend data source on cache events
2. Configure, create and register the listener with an `AsyncEventQueue` (AEQ)
3. Create a Region serving as the source of cache events and attach the AEQ

The advantage of this approach is you have access to and control over low-level configuration details. The disadvantage,
of course, is with more moving parts, it is easier to mess things up.

Following on from our synchronous (_Read/Write-Through_) _Inline Caching_ examples from the prior sections above,
our `AsyncEventListener` implementation might appear as follows:

.Example `AsyncEventListener` for Async _Inline Caching_
[source,java]
----
@Component
class ExampleAsyncEventListener implements AsyncEventListener {

	private final DataSource dataSource;

	ExampleAsyncEventListener(DataSoruce dataSource) {
		this.dataSource = dataSource;
	}

	@Override
	public boolean processEvents(List<AsyncEvent> events) {

		// Iterate over the ordered AsyncEvents and use the DataSource
        // to write to the external, backend DataSource

	}
}
----

NOTE: Instead of injecting a `DataSource` into your `AsyncEventListener` directly, you could use JDBC,
Spring's `JdbcTemplate`, JPA/Hibernate or another data access API/Framework. Further below, we will show how SBDG
simplifies the `AsyncEventListener` implementation by using Spring Data _Repositories_.

Then, we need to register this listener with a `AsyncEventQueue` (AEQ) (#2) and attach it to the target Region
that will be the source of the cache events we want to persist asynchronously (#3):

.Configure and Create an `AsyncEventQueue`
[source,java]
----
@Configuration
@PeerCacheApplication
class GeodeConfiguration {

	@Bean
    DataSource exampleDataSource() {
		// Configure and construct a data store specific DataSource
    }

	@Bean
    ExampleAsyncEventListener exampleAsyncEventListener(DataSource dataSource) {
		return new ExampleAsyncEventListener(dataSource);
    }

	@Bean
    AsyncEventQueueFactoryBean exampleAsyncEventQueue(Cache peerCache, ExampleAsyncEventListener listener) {

		AsyncEventQueueFactoryBean asyncEventQueue = new AsyncEventQueueFactoryBean(peerCache, listener);

		asyncEventQueue.setBatchConflationEnabled(true);
		asyncEventQueue.setBatchSize(50);
		asyncEventQueue.setBatchTimeInterval(15000); // 15 seconds
        asyncEventQueue.setMaximumQueueMemory(64); // 64 MB
        // ...

		return asyncEventQueue;
    }

    @Bean("Example")
    PartitionedRegionFactoryBean<?, ?> exampleRegion(Cache peerCache, AsyncEventQueue queue) {

        PartitionedRegionFactoryBean<?, ?> exampleRegion = new PartitionedRegionFactoryBean<>();

        exampleRegion.setAsyncEventQueues(ArrayUtils.asArray(queue));
        exampleRegion.setCache(peerCache);
        // ...

        return exampleRegion;
    }
}
----

While this approach affords you the developer a lot of control over the (low-level) configuration, in addition to
your `AsyncEventListener` implementation, this is a lot of boilerplate code.

TIP: See the {spring-data-geode-javadoc}/org/springframework/data/gemfire/wan/AsyncEventQueueFactoryBean.html[Javadoc]
on SDG's `AsyncEventQueueFactoryBean` for more details on the configuration of the AEQ.

TIP: See {geode-name}'s {apache-geode-docs}/developing/events/implementing_write_behind_event_handler.html[User Guide]
for more details on AEQs and listeners.

Fortunately, with SBDG, there is a better way!

[[geode-caching-provider-inline-caching-asynchronous-using-spring-data-repositories]]
====== Asynchronous Inline Caching using Spring Data Repositories

The implementation and configuration of the `AsyncEventListener` as well as the AEQ shown above can be simplified
as follows:

.Using SBDG to configure Asynchronous (Write-Behind) Inline Caching
[source,java]
----
@SpringBootApplication
@EntityScan(basePackageClasses = ExampleEntity.class)
@EnableJpaRepositories(basePackageClasses = ExampleRepository.class)
@EnableEntityDefinedRegions(basePackageClasses = ExampleEnity.class)
class ExampleSpringBootApacheGeodeAsyncInlineCachingApplication {

	@Bean
    AsyncInlineCachingRegionConfigurer asyncInlineCachingRegionConfigurer(
    	    CrudRepository<ExampleEntity, Long> repository) {

		return AsyncInlineCachingRegionConfigurer.create(repository, "Example")
            .withQueueBatchConflationEnabled()
            .withQueueBatchSize(50)
            .withQueueBatchTimeInterval(Duration.ofSeconds(15))
            .withQueueMaxMemory(64);
    }
}
----

The `AsyncInlineCachingRegionConfigurer.create(..)` method is overloaded to accept a `Predicate` in place of the `String`
in order to express more powerful matching logic, programmatically, identifying the target Region (by name) on which to
configure asynchronous _Inline Caching_ functionality.

The `AsyncInlineCachingRegionConfigurer` uses the https://en.wikipedia.org/wiki/Builder_pattern[_Builder Software Design Pattern_]
and `withQueue*(..)` builder methods to configure the underlying `AsyncEventQueue` (AEQ) when the queue's configuration
deviates from the defaults, as specified by {geode-name}.

Under-the-hood, the `AsyncInlineCachingRegionConfigurer` constructs a new instance of the `RepositoryAsyncEventListener`
class initialized with the given Spring Data `CrudRepository`. The `RegionConfigurer` then registers the listener with
the AEQ and attaches it to the target `Region`.

With the power of Spring Boot _auto-configuration_ and SBDG, the configuration is much more concise and intuitive.

[[geode-caching-provider-inline-caching-asynchronous-listener]]
====== About `RepositoryAsyncEventListener`

The SBDG `RepositoryAsyncEventListener` class is the magic sauce behind the integration of the cache with an external,
backend data source.

The listener is a specialized https://en.wikipedia.org/wiki/Adapter_pattern[Adpater] that processes `AsyncEvents` by
invoking an appropriate `CrudRepository` method based on the cache operation. The listener requires an instance of
`CrudRepository`. As such, the listener supports any external, backend data source supported by Spring Data's
_Repository_ abstraction.

Of course, backend data store, data access operations (e.g. INSERT, UPDATE, DELETE, etc) triggered by cache events
are performed asynchronously from the cache operation. This means the state of the cache and backend data store
will be "_eventually consistent_".

ERROR HANDLING

Given the complex nature of "_eventually consistent_" systems and asynchronous concurrent processing, the
`RepositoryAsyncEventListener` allows users to register a custom `AsyncEventErrorHandler` to handle the errors
that occur during processing of `AsyncEvents`, perhaps due to a faulty backend data store data access operation
(e.g. `OptimisticLockingFailureException`), in an application relevant way.

The `AsyncEventErrorHandler` interface is a `java.util.function.Function` implementation and `@FunctionalInterface`
defined as:

.AsyncEventErrorHandler interface definition
[source,java]
----
@FunctionalInterface
interface AsyncEventErrorHandler implements Function<AsyncEventError, Boolean> { }
----

The `AsyncEventError` class encapsulates `AsyncEvent` along with the `Throwable` that was thrown while processing
the event.

Since the `AsyncEventErrorHandler` interface implements `Function`, then you would override the `apply(:AsyncEventError)`
method to handle the error with application-specific actions. The handler returns a `Boolean` to indicate whether it was
able to handle the error or not.

.Custom `AsyncEventErrorHandler` implementation
[source,java]
----
class CustomAsyncEventErrorHandler implements AsyncEventErrorHandler {

	@Override
    public Boolean apply(AsyncEventError error) {

		if (error.getCause() instanceof OptimisticLockingFailureException) {
			// handle optimistic locking failure if you can
            return true; // if error was successfully handled.
		}
		else if (error.getCause() instanceof IncorrectResultSizeDataAccessException) {
			// handle no row or too many row update if you can
            return true; // if error was successfully handled.
		}

		return false;
    }
}
----

It is easy to configure the `RepositoryAsyncEventListener` with your custom `AsyncEventErrorHandler` using the
`AsyncInlineCachingRegionConfigurer`, like so:

.Configuring a custom `AsyncEventErrorHandler`
[source,java]
----
@Configuration
class GeodeConfiguration {

	@Bean
	CustomAsyncEventErrorHandler customAsyncEventErrorHandler() {
		return new CustomAsyncEventErrorHandler();
	}

	@Bean
    AsyncInlineCachingRegionConfigurer asyncInlineCachingRegionConfigurer(
    	    CrudRepository<?, ?> repository,
            CustomAsyncEventErrorHandler errorHandler
    ) {

		return AsyncInlineCachingRegionConfigurer.create(repository, "Example")
            .withAsyncEventErrorHandler(errorHandler);
    }
}
----

Also, since `AsyncEventErrorHandler` implements `Function`, you can https://en.wikipedia.org/wiki/Composite_pattern["_compose_"]
multiple error handlers using {jdk-javadoc}/java/util/function/Function.html#andThen-java.util.function.Function-[`Function.andThen(:Function)`].

SUPPORTED CACHE OPERATIONS

By default, the `RepositoryAsyncEventListener` handles `CREATE`, `UPDATE` and `REMOVE` cache event, entry operations.

`CREATE` and `UPDATE` translates to `CrudRepository.save(entity)` where the `entity` is derived from
`AsyncEvent.getDeserializedValue()`.

`REMOVE` translates to `CrudRepository.delete(entity)` where the `entity` is derived from
`AsyncEvent.getDeserializedValue()`.

The cache {apache-geode-javadoc}/org/apache/geode/cache/Operation.html[`Operation`] to `CrudRepository` method is
supported by the `AsyncEventOperationRepositoryFunction` interface, which implements `java.util.function.Function`
and is a `@FunctionalInterface`.

This interface becomes useful if and when you want to implement `CrudRepository` method invocations for other
`AsyncEvent` `Operations` not handled by SBDG's `RepositoryAsyncEventListener` out-of-the-box.

The `AsyncEventOperationRepositoryFunction` interface is defined as:

.AsyncEventOperationRepositoryFunction interface definition
[source,java]
----
@FunctionalInterface
interface AsyncEventOperationRepositoryFunction<T, ID> implements Function<AsyncEvent<ID, T>,  Boolean> {

	default boolean canProcess(AsyncEvent<ID, T> event) {
		return false;
	}
}
----

`T` is the class type of the entity and `ID` is the class type of the entity's identifier (ID), possibly declared with
Spring Data's {spring-data-commons-javadoc}/org/springframework/data/annotation/Id.html[`org.springframework.data.annotation.Id`] annotation.

For convenience, SBDG provides the `AbstractAsyncEventOperationRepositoryFunction` class for extension, where you would
provide implementations for the `cacheProcess(:AsyncEvent)` and `doRepositoryOp(entity)` methods.

NOTE: The `AsyncEventOperationRepositoryFunction.apply(:AsyncEvent)` method is already implemented in terms of
`canProcess(:AsyncEvent)`, `resolveEntity(:AsyncEvent)`, `doRepositoryOp(entity)`, and catching and handling any
`Throwable` (errors) by calling the configured `AsyncEventErrorHandler`.

For example, you might want to handle {apache-geode-javadoc}/org/apache/geode/cache/Operation.html#INVALIDATE[`Operation.INVALIDATE`]
cache events as well, deleting the entity from the backend data store by invoking the `CrudRepository.delete(entity)`
method:

.Handling `AsyncEvent`, `Operation.INVALIDATE`
[source,java]
----
@Component
class InvalidateAsyncEventRepositoryFunction
    extends RepositoryAsyncEventListener.AbstractAsyncEventOperationRepositoryFunction<?, ?> {

	InvalidateAsyncEventRepositoryFunction(RepositoryAsyncEventListener<?, ?> listener) {
		super(listener);
	}

	@Override
	public boolean canProcess(AsyncEvent<?, ?> event) {
		return event != null && Operation.INVALIDATE.equals(event.getOperation());
	}


	@Override
	protected Object doRepositoryOperation(Object entity) {
		getRepository().delete(entity);
		return null;
	}
}
----

You can then register your user-defined, `AsyncEventOperationRepositoryFunction`
(i.e. `InvalidateAsyncEventRepositoryFunction`) with the `RepositoryAsyncEventListener` by using the
`AsyncInlineCachingRegionConfigurer`, like so:

.Configuring a user-defined `AsyncEventOperationRepositoryFunction`
[source,java]
----
import org.springframework.geode.cache.RepositoryAsyncEventListener;@Configuration
class GeodeConfiguration {

	@Bean
    AsyncInlineCachingRegionConfigurer asyncInlineCachingRegionConfigurer(
    	    CrudRepository<?, ?> repository,
            CustomerAsyncEventErrorHandler errorHandler
    ) {

		return AsyncInlineCachingRegionConfigurer.create(repository, "ExampleRegion")
            .applyToListener(listener -> {

            	if (listener instanceof RepositoryAsyncEventListener) {

            		RepositoryAsyncEventListener<?, ?> repositoryListener =
                        (RepositoryAsyncEventListener<?, ?>) listener;

             		repositoryListener.register(new InvalidAsyncEventRepositoryFunction(repositoryListener));
            	}

            	return listener;
            });
    }
}
----

This same technique can be applied to `CREATE`, `UPDATE` and `REMOVE` cache  operations as well, effectively overriding
the default behavior for this cache operations handled by SBDG out-of-the-box.

[[geode-caching-provider-inline-caching-asynchronous-region-configurer]]
====== About `AsyncInlineCachingRegionConfigurer`

As we saw in the previous section, it is possible to intercept and post-process key components constructed
and configured by the `AsyncInlineCachingRegionConfigurer` class during initialization.

Out-of-the-box, SBDG's allows you to intercept and post-process the `AsyncEventListener` (e.g. `RepositoryAsyncEventListener`),
`AsyncEventQueueFactory` and even the `AsyncEventQueue`, created by the `AsyncInlineCachingRegionConfigurer`
(a SDG {spring-data-geode-javadoc}/org/springframework/data/gemfire/config/annotation/RegionConfigurer.html[`RegionConfigurer`])
during Spring `ApplicationContext`, bean initialization.

The `AsyncInlineCachingRegionConfigurer` class provides the builder methods listed below to intercept and post-process
any of the following {geode-name} objects:

* `applyToListener(:Function<AsyncEventListener, AsyncEventListener>)`
* `applyToQueue(:Function<AsyncEventQueue, AsyncEventQueue>)`
* `applyToQueueFactory(:Function<AsyncEventQueueFactory, AsyncEventQueueFactory>)`

All of these "_apply_" methods accept a `java.util.function.Function` that "_applies_" the logic of the `Function` to
the {geode-name} object (e.g. `AsyncEventListener`), returning the object as a result.

TIP: The {geode-name} object returned by the `Function` may be the same object, a proxy, or a completely new object.
Essentially, the returned object can be anything you want. This is the fundamental premise behind
_Aspect-Oriented Programming_ (AOP) and the https://en.wikipedia.org/wiki/Decorator_pattern[Decorator Software Design Pattern].

These "_apply_" methods and the supplied `Function` allow you to decorate, enhance, post-process, whatever you want to,
to the {geode-name} objects created by the listener.

Of course, the `AsyncInlineCachingRegionConfigurer` strictly adheres to the https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle[Open/Close Principle]
as well, and is therefore flexibly extensible.

[[geode-caching-provider-multi-site-caching]]
==== Multi-Site Caching

The final pattern of caching presented in this chapter is _Multi-Site Caching_.

As described above, there are 2 configuration arrangements depending on your application usage patterns, requirements
and user demographic: _Active-Active_ & _Active-Passive_.

_Multi-Site Caching_ along with _Active-Active_ and _Active-Passive_ configuration arrangements will be described
in more detail in the Sample link:guides/caching-multi-site.html[Guide]. Also, be sure to review the Sample
{github-samples-url}/caching/multi-site[Code].

[[geode-caching-provider-advanced-configuration]]
=== Advanced Caching Configuration

{geode-name} supports additional caching capabilities to manage the entries stored in the cache.

As you can imagine, given that cache entries are stored in-memory, it becomes important to monitor and manage the
available memory wisely.  After all, by default, {geode-name} stores data in the JVM Heap.

Several techniques can be employed to more effectively manage memory, such as using
{apache-geode-docs}/developing/eviction/chapter_overview.html[Eviction], possibly
{apache-geode-docs}/developing/storing_data_on_disk/chapter_overview.html[overflowing data to disk],
configuring both entry _Idle-Timeout_ (TTI) as well as _Time-To-Live_ (TTL)
{apache-geode-docs}/developing/expiration/chapter_overview.html[Expiration policies],
configuring {apache-geode-docs}/managing/region_compression.html[Compression],
and using {apache-geode-docs}/managing/heap_use/off_heap_management.html[Off-Heap], or main memory.

There are several other strategies that can be used as well, as described in
{apache-geode-docs}/managing/heap_use/heap_management.html[Managing Heap and Off-heap Memory].

While this is well beyond the scope of this document, know that Spring Data for {geode-name} makes all of these
{spring-data-geode-docs-html}/#bootstrap-annotation-config-regions[configuration options] available and simple to use.

[[geode-caching-provider-disable]]
=== Disable Caching

There may be cases where you do not want your Spring Boot application to cache application state with
{spring-framework-docs}/integration.html#cache[Spring's Cache Abstraction] using {geode-name}. In certain cases, you
may be using another Spring supported caching provider, such as Redis, to cache and manage your application state,
while, even in other cases, you may not want to use Spring's Cache Abstraction at all.

Either way, you can specifically call out your Spring Cache Abstraction provider using the `spring.cache.type` property
in `application.properties`, as follows:

.Use Redis as the Spring Cache Abstraction Provider
[source,txt]
----
#application.properties

spring.cache.type=redis
...
----

If you prefer not to use Spring's Cache Abstraction to manage your Spring Boot application's state at all, then
do the following:

.Disable Spring's Cache Abstraction
[source,txt]
----
#application.properties

spring.cache.type=none
...
----

See Spring Boot {spring-boot-docs-html}/boot-features-caching.html#boot-features-caching-provider-none[docs]
for more details.

TIP: It is possible to include multiple providers on the classpath of your Spring Boot application.  For instance,
you might be using Redis to cache your application's state while using {geode-name} as your application's persistent
data store (_System of Record_).

NOTE: Spring Boot does not properly recognize `spring.cache.type=[gemfire|geode]` even though Spring Boot
for {geode-name} is setup to handle either of these property values (i.e. either "`gemfire`" or "`geode`").
