0

I am using Spring Data for Apache Geode for a Spring Boot app that uses a remote Apache Geode server. To set up local Regions I am using @Configuration class like:

@Configuration
public class GeodeConfig {

  public static Region<String, String> myRegion;

  @Bean("setupMyRegion")
  public Region<String, String> setupMyRegion(GemFireCache cache) {

    Region<String, String> r = cache.getRegion("myRegion");
    myRegion = r;

    return r;
  }
}

This enables me to put and get data with the Region.

When I register interest in subscribing to a key, the subscription is setup with scope=LOCAL like this:

org.apach.geode.inter.cache.LocalRegion 4439 logKeys: org.apache.geode.internal.cache.LocalRegion[path='/myRegion';scope=LOCAL';dataPolicy=NORMAL; concurrencyChecksEnabled] refreshEntriesFromServerKeys count=1 policy=KEYS_VALUES

and I guess that I want to set it up as a CACHING_PROXY so that later, on subscribing to events in the Region, the local subscription will see the server Region events.

I've set subscriptionEnabled=TRUE for the @ClientCacheApplication and I am trying to set

clientRegionShortcut = ClientRegionShortcut.CACHING_PROXY 

I don't know where to do this. I've tried the @EnableClusterDefinedRegions annotation but the Region is still being set to local.

If I try to create a Region Factory from the GemFireCache it gives me an error that the method createClientRegionFactory(ClientRegionShortcut) is "undefined" for the type GemFireCache.

THANKS

John Blum
  • 7,381
  • 1
  • 20
  • 30
rupweb
  • 3,052
  • 1
  • 30
  • 57

2 Answers2

3

Generally, it is bad practice to use the Apache Geode API directly to configure beans in a Spring context. For example...

  @Bean("MyRegion")
  public Region<String, Object> myRegion(GemFireCache cache) {
    return cache.getRegion("myRegion");
  }

Or using:

  @Bean("MyRegion")
  public Region<String, Object> myRegion(ClientCache clientCache) {
    return clientCache.createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY)
      .create("MyRegion");
  }

Both approaches above are not recommended!

Spring Data for Apache Geode (SDG) contains specific Spring FactoryBeans to do the same thing. For instance, the SDG ClientRegionFactoryBean.

You would use the ClientRegionFactoryBean in your Spring application configuration as follows:

@Bean("MyRegion")
ClientRegionFactoryBean<String, Object> myRegion(ClientCache clientCache) {

  ClientRegionFactoryBean<String, Object> myRegion = 
    new ClientRegionFactoryBean<>();

  myRegion.setCache(clientCache);
  myRegion.setShortcut(ClientRegionShortcut.CACHING_PROXY);

  return myRegion;
}

There are SDG FactoryBeans for different types of Geode objects, such a [client] Regions, Indexes, DiskStores, GatewayReceivers, GatewaySenders, etc.

These FactoryBeans are essential in ensuring the the "lifecycle" of the Geode objects (e.g. Region(s)) are properly synced to the lifecycle of the Spring container. By forgoing the use of SDG's FactoryBeans you are effectively ignoring the lifecycle coordination provided by the Spring container and custom (framework) FactoryBeans, and are therefore responsible for the lifecycle of the Geode objects themselves when using Geode's API direct in Spring config. This is one of the primary reasons SDG's exists in the first place.

A "client" Region must be a *PROXY (e.g. PROXY or CACHING_PROXY) in order to enable subscriptions and receive interesting events from servers for that Region. This is because the Geode cluster (servers) maintain a subscription queue for clients when they connect, which contain events that the client has expressed interests in receiving.

For *PROXY client Regions, this also means that the client Region must have a corresponding server-side, peer cache Region by the same name.

You can simplify Region creation in general, and client Region creation in particular, by using SDG's Annotation-based configuration model, and specifically, @EnableEntityDefinedRegions or @EnableCachingDefinedRegions.

For instance:

@ClientCacheApplication(subscriptionsEnabled = true)
@EnableEntityDefinedRegions(basePackageClasses = SomeEntity.class, 
  clientRegionShortcut = ClientRegionShortcut.CACHING_PROXY)
class GeodeConfiguration {
  ...
}

Of course, this assumes you have defined application domain model entity classes, such as:

package example.app.model;

import ...;

@Region("MyRegion")
class SomeEntity {

  @Id
  private Long id;

  ...
}

The ClientRegionFactoryBean would be typed accordingly: ClientRegionFactoryBean<Long, SomeEntity>.

See the SDG Annotation-based configuration model documentation for more details, and specifically Annotation configuration covering Region definitions.

The @EnableClusterDefinedRegions annotation defines client-side Regions for all existing server-side (cluster) Regions to which the client connects.

For example, if you start a cluster in Gfsh as follows:

gfsh>start locator --name=MyLocator ...

gfsh>start server --name=MyServer ...

gfsh>create region --name=Example --type=PARTITION

gfsh>create region --name=AnotherExample --type=REPLICATE

And then connect a Spring Boot, Apache Geode ClientCache application to this cluster by using the following Geode configuration:

@ClientCacheApplication
@EnableClusterDefinedRegions(clientRegionShortcut = 
  ClientRegionShortcut.CACHING_PROXY)
class GeodeConfiguration {
  ...
}

Then the Spring Boot, Apache Geode ClientCache application would have 2 Spring configured client Region beans with names "Example" and "AnotherExample" both of type, CACHING_PROXY.

If there are NO server-side Regions presently in the cluster, then the @EnableClusterDefinedRegions would not define any client-side Regions.

Anyway, I hope this makes sense and helps!

Cheers!

rupweb
  • 3,052
  • 1
  • 30
  • 57
John Blum
  • 7,381
  • 1
  • 20
  • 30
  • thanks John, in the last example using @EnableClusterDefinedRegions, what additional variable setup do you need in the spring boot client to put, get to the AnotherExample region.... and register for interesting events from that region... without the client throwing a compile time error: what is this unknown region variable? In other words do you still have to declare a ClientRegionRegionFactoryBean for AnotherExample to use it on the spring boot client? – rupweb Feb 12 '20 at 22:35
  • 1
    When using the `@EnableClusterDefinedRegions` annotation, you do not need to explicitly declare beans in the *Spring* context of type `ClientRegionFactoryBean` for all server-side Regions, in order to have a corresponding client-side [*PROXY] Region by the same name. That is the convenience provided by the annotation. You could then inject the client Region bean into any application component managed by *Spring* using the standard autowiring, `@Resource(name = "Example") private Region example`. Continued... – John Blum Feb 13 '20 at 03:08
  • 1
    The same applies for "AnotherExample" Region and any other server-side Region. When using SBDG, SBDG also provides a `GemfireTemplate` bean for each (client) Region defined. See SBDG's `GemfireTemplate` support (https://docs.spring.io/spring-boot-data-geode-build/1.3.x/reference/html5/#geode-data-access-region-templates). See here (https://docs.spring.io/spring-data/geode/docs/current/reference/html/#apis:template) and here (https://docs.spring.io/spring-data/geode/docs/current/api/org/springframework/data/gemfire/GemfireTemplate.html) for more details about the SDG `GemfireTemplate` class. – John Blum Feb 13 '20 at 03:12
  • Regarding... *what additional variable setup do you need in the spring boot client to put, get to the AnotherExample region* ... You simply need to inject either the Region bean or the GemfireTemplate for the Region to perform data access operations on that Region (e.g. "AnotherExample"). – John Blum Feb 13 '20 at 03:14
  • 1
    To "register interests", if you have a reference to the Region (as in injected bean), then you could use Geode's Region API to register interests (e.g. https://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/Region.html#registerInterest-K-boolean-boolean-). Alternatively, and recommended, you could declare a `RegionConfigurer` bean (https://docs.spring.io/spring-data/geode/docs/current/api/org/springframework/data/gemfire/config/annotation/RegionConfigurer.html), filter by beanName (e.g. "AnotherExample") and then register interests using the `ClientRegionFactoryBean` API... – John Blum Feb 13 '20 at 03:18
  • 1
    Using: https://docs.spring.io/spring-data/geode/docs/current/api/org/springframework/data/gemfire/client/ClientRegionFactoryBean.html#setInterests-org.springframework.data.gemfire.client.Interest:A- – John Blum Feb 13 '20 at 03:19
  • 1
    Regarding... *do you still have to declare a ClientRegionFactoryBean for AnotherExample to use it on the spring boot client?* NO. See SDG's test suite for a an example of using the `@EnableClusterDefinedRegions` annotation (https://github.com/spring-projects/spring-data-geode/blob/master/spring-data-geode/src/test/java/org/springframework/data/gemfire/config/annotation/EnableClusterDefinedRegionsIntegrationTests.java). This test is not registering interests on any client Region auto-configured by the `@EnableClusterDefinedRegions` ... – John Blum Feb 13 '20 at 03:23
  • 1
    ... , but it would be a simple matter to do so using the `RegionConfigurer` as I mentioned above. – John Blum Feb 13 '20 at 03:23
  • Hi John, I try the @Resource injection in a worker class and get null pointer. It’s not init’d... aargh.. also if using the *not* recommended clientCache.create approach at the top, I can put and get to client and server region, but if I try and register interest in a key and put a key into the server region the spring boot client region isn’t seeing the event.... I’m gonna try and get a region factory going but if you have any comment, it’s appreciated. – rupweb Feb 13 '20 at 21:24
  • client is a spring boot geode client and server is gfsh standard Geode server on a remote box..... – rupweb Feb 13 '20 at 21:25
0

The main problem here was that the class in which I wanted to use the Geode regions was not registering in the Spring container because I was using new to use that class instead of using @Autowired annotation for Spring. That meant that when I setup the Geode regions per @JohnBlum's answer using @Resource(name = "Example") private Region<Object, Object> example the autowiring was still producing null objects.

Also the class where I wanted to use the regions was in another package outside of my @ComponentScan so I had to amend the @ComponentScan to find that package.

Thing is, once I got through these configurations and properly setup the @EnableClusterDefinedRegions (real easy once you know how!!) and I can put and get with the regions, the register interests doesn't work.

The problem with the register interests was that my application.properties had

spring.data.gemfire.cache.client.durable-client-id=myListener

and when I removed that property the CacheListenerAdapter started to receive events from the server!

rupweb
  • 3,052
  • 1
  • 30
  • 57