Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Infinispan caches on first access with the minimal config #27838

Merged
merged 1 commit into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
67 changes: 55 additions & 12 deletions docs/src/main/asciidoc/infinispan-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,17 @@ Add the following properties to connect to Infinispan Server:

[source,properties]
----
# Infinispan Server address
quarkus.infinispan-client.server-list=localhost:11222
quarkus.infinispan-client.server-list=localhost:11222 <1>

# Authentication
quarkus.infinispan-client.auth-username=admin
quarkus.infinispan-client.auth-password=password
quarkus.infinispan-client.auth-username=admin <2>
quarkus.infinispan-client.auth-password=password <3>

# Infinispan client intelligence
# Use BASIC as a Docker for Mac workaround
quarkus.infinispan-client.client-intelligence=BASIC
quarkus.infinispan-client.client-intelligence=BASIC <4>
----
<1> Sets Infinispan Server address list, separated with commas
<2> Sets the authentication username
<3> Sets the authentication password
<4> Sets the client intelligence. Use BASIC as a workaround if using Docker for Mac.

.Running Infinispan Server

Expand All @@ -90,6 +90,31 @@ Infinispan Server also enables authentication and security authorization by defa
$ ./bin/cli.sh user create admin -p password
----

=== Creating caches from the client

When a cache is accessed from the client, if the cache does not exist in the Infinispan Server and you want
to create it on first access, use one of the following properties:

[source,properties]
----
quarkus.infinispan-client.cache.books.configuration-uri=cacheConfig.xml <1>
quarkus.infinispan-client.cache.magazine.configuration=<distributed-cache><encoding media-type="application/x-protostream"/></distributed-cache> <2>
----
<1> The file name located under the `resources` folder that contains the configuration of the 'books' cache
<2> The configuration of the 'magazine' cache as a plain text property

If both `configuration-uri` and `configuration` are configured for the same cache with the same Quarkus profile,
`configuration-uri` gets preference over `configuration`.

If nothing is configured for a particular cache, it will be created with the following basic configuration:

[source, xml]
----
<distributed-cache>
<encoding media-type="application/x-protostream"/>
</distributed-cache>
----

=== Authentication mechanisms

You can use the following authentication mechanisms with the Infinispan client:
Expand Down Expand Up @@ -478,10 +503,28 @@ You can read more about https://infinispan.org/docs/stable/titles/developing/dev

== Near Caching

Near caching is disabled by default, but you can enable it by setting the profile config property
`quarkus.infinispan-client.near-cache-max-entries` to a value greater than 0. You can also configure
a regular expression so that only a subset of caches have near caching applied through the
`quarkus.infinispan-client.near-cache-name-pattern` attribute.
Near caching is disabled by default, but you can enable it on a per cache basic by configuring the following properties:

[source,properties]
----
quarkus.infinispan-client.cache.books.near-cache-mode=INVALIDATED <1>
quarkus.infinispan-client.cache.books.near-cache-max-entries=200 <2>
quarkus.infinispan-client.cache.books.near-cache-use-bloom-filter=true <3>
----

<1> Enables near caching for the 'books' cache by setting the mode to `INVALIDATED`
<2> Sets the maximum number of entries that the near cache of the 'books' cache can hold before eviction occurs
<3> Enables bloom filter for the 'books' cache

=== Bounded near caching

You should always use bounded near caches by specifying the maximum number of entries they can contain.

=== Bloom filters

If you need to optimize the performance for write operations by reducing the total number of invalidation messages,
enable bloom filter. Bloom filters reside on Infinispan Server and keep track of the entries that the client has requested.
They cannot be used with unbounded near cache: maximum number of entries must be defined when enabling bloom filters.

== Encryption

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.infinispan.client.hotrod.impl.ConfigurationProperties;
import org.infinispan.client.hotrod.logging.Log;
import org.infinispan.client.hotrod.logging.LogFactory;
import org.infinispan.commons.configuration.XMLStringConfiguration;
import org.infinispan.commons.marshall.Marshaller;
import org.infinispan.commons.marshall.ProtoStreamMarshaller;
import org.infinispan.commons.util.Util;
Expand All @@ -43,6 +44,7 @@
public class InfinispanClientProducer {
private static final Log log = LogFactory.getLog(InfinispanClientProducer.class);

public static final String DEFAULT_CONFIG = "<distributed-cache><encoding media-type=\"application/x-protostream\"/></distributed-cache>";
public static final String PROTOBUF_FILE_PREFIX = "infinispan.client.hotrod.protofile.";
public static final String PROTOBUF_INITIALIZERS = "infinispan.client.hotrod.proto-initializers";

Expand Down Expand Up @@ -324,20 +326,33 @@ public <K, V> RemoteCache<K, V> getRemoteCache(InjectionPoint injectionPoint, Re
final io.quarkus.infinispan.client.Remote remote = getRemoteAnnotation(annotationSet);

if (cacheManager != null && remote != null && !remote.value().isEmpty()) {
return cacheManager.getCache(remote.value());
RemoteCache<K, V> cache = cacheManager.getCache(remote.value());
if (cache == null) {
log.warn("Attempt to create cache using minimal default config");
return cacheManager.administration()
.getOrCreateCache(remote.value(), new XMLStringConfiguration(DEFAULT_CONFIG));
}
return cache;
}

if (cacheManager != null) {
return cacheManager.getCache();
RemoteCache<K, V> cache = cacheManager.getCache();
if (cache == null) {
log.warn("Attempt to create cache using minimal default config");
return cacheManager.administration()
.getOrCreateCache(remote.value(), new XMLStringConfiguration(DEFAULT_CONFIG));
}
return cache;
}

log.error("Unable to produce RemoteCache. RemoteCacheManager is null");
return null;
}

@Produces
public CounterManager counterManager() {
RemoteCacheManager cacheManager = remoteCacheManager();
public CounterManager counterManager(RemoteCacheManager cacheManager) {
if (cacheManager == null) {
log.error("Unable to produce CounterManager. RemoteCacheManager is null");
return null;
}
return RemoteCounterManagerFactory.asCounterManager(cacheManager);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.infinispan.query.dsl.Query;
import org.infinispan.query.dsl.QueryFactory;

import io.quarkus.infinispan.client.Remote;
import io.quarkus.runtime.StartupEvent;

@ApplicationScoped
Expand All @@ -40,17 +41,23 @@ public class CacheSetup {

public static final String DEFAULT_CACHE = "default";
public static final String MAGAZINE_CACHE = "magazine";
public static final String AUTHORS_CACHE = "authors";

@Inject
RemoteCacheManager cacheManager;

@Inject
@Remote(AUTHORS_CACHE)
RemoteCache<String, Author> authors;

private final Map<String, Book> matches = new ConcurrentHashMap<>();

private CountDownLatch waitUntilStarted = new CountDownLatch(1);

void onStart(@Observes StartupEvent ev) {
RemoteCache<String, Book> defaultCache = cacheManager.getCache(DEFAULT_CACHE);
RemoteCache<String, Magazine> magazineCache = cacheManager.getCache(MAGAZINE_CACHE);

defaultCache.addClientListener(new EventPrintListener());

ContinuousQuery<String, Book> continuousQuery = Search.getContinuousQuery(defaultCache);
Expand Down Expand Up @@ -81,8 +88,10 @@ public void resultUpdated(String key, Book value) {

log.info("Added continuous query listener");

Author gMartin = new Author("George", "Martin");

defaultCache.put("book1", new Book("Game of Thrones", "Lots of people perish", 2010,
Collections.singleton(new Author("George", "Martin")), Type.FANTASY, new BigDecimal("23.99")));
Collections.singleton(gMartin), Type.FANTASY, new BigDecimal("23.99")));
defaultCache.put("book2", new Book("Game of Thrones Path 2", "They win?", 2023,
Collections.singleton(new Author("Son", "Martin")), Type.FANTASY, new BigDecimal("54.99")));

Expand All @@ -94,6 +103,8 @@ public void resultUpdated(String key, Book value) {
magazineCache.put("popular-time", new Magazine("TIME", YearMonth.of(1997, 4),
Arrays.asList("Yep, I'm gay", "Backlash against HMOS", "False Hope on Breast Cancer?")));

authors.put("aut-1", gMartin);

waitUntilStarted.countDown();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public class TestServlet {
@Remote(CacheSetup.MAGAZINE_CACHE)
RemoteCache<String, Magazine> magazineCache;

@Inject
@Remote(CacheSetup.AUTHORS_CACHE)
RemoteCache<String, Author> authorsCache;

@Inject
CounterManager counterManager;

Expand Down Expand Up @@ -234,4 +238,13 @@ public String magazineQuery(@PathParam("id") String name) {
.map(m -> m.getName() + ":" + m.getPublicationYearMonth())
.collect(Collectors.joining(",", "[", "]"));
}

@Path("create-cache-default-config/authors")
@GET
public String magazineQuery() {
cacheSetup.ensureStarted();
return authorsCache.values().stream()
.map(a -> a.getName())
.collect(Collectors.joining(",", "[", "]"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,9 @@ public void testNearCacheInvalidation() {
public void testQueryWithCustomMarshaller() {
RestAssured.when().get("/test/magazinequery/IM").then().body(is("[TIME:1923-03,TIME:1997-04]"));
}

@Test
public void testAuthor() {
RestAssured.when().get("/test/create-cache-default-config/authors").then().body(is("[George]"));
}
}