diff --git a/docs/src/main/asciidoc/infinispan-client.adoc b/docs/src/main/asciidoc/infinispan-client.adoc index 46ae2b62bc806..3a01a42383fb2 100644 --- a/docs/src/main/asciidoc/infinispan-client.adoc +++ b/docs/src/main/asciidoc/infinispan-client.adoc @@ -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 @@ -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= <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] +---- + + + +---- + === Authentication mechanisms You can use the following authentication mechanisms with the Infinispan client: @@ -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 diff --git a/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanClientProducer.java b/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanClientProducer.java index 7b8c52d3ffbf4..f59c6dbd49ac9 100644 --- a/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanClientProducer.java +++ b/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanClientProducer.java @@ -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; @@ -43,6 +44,7 @@ public class InfinispanClientProducer { private static final Log log = LogFactory.getLog(InfinispanClientProducer.class); + public static final String DEFAULT_CONFIG = ""; public static final String PROTOBUF_FILE_PREFIX = "infinispan.client.hotrod.protofile."; public static final String PROTOBUF_INITIALIZERS = "infinispan.client.hotrod.proto-initializers"; @@ -324,20 +326,33 @@ public RemoteCache 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 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 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); diff --git a/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/CacheSetup.java b/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/CacheSetup.java index f04c346a7fea5..b12c898043842 100644 --- a/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/CacheSetup.java +++ b/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/CacheSetup.java @@ -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 @@ -40,10 +41,15 @@ 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 authors; + private final Map matches = new ConcurrentHashMap<>(); private CountDownLatch waitUntilStarted = new CountDownLatch(1); @@ -51,6 +57,7 @@ public class CacheSetup { void onStart(@Observes StartupEvent ev) { RemoteCache defaultCache = cacheManager.getCache(DEFAULT_CACHE); RemoteCache magazineCache = cacheManager.getCache(MAGAZINE_CACHE); + defaultCache.addClientListener(new EventPrintListener()); ContinuousQuery continuousQuery = Search.getContinuousQuery(defaultCache); @@ -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"))); @@ -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(); } diff --git a/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/TestServlet.java b/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/TestServlet.java index a1cec8db1b943..7352e9f637d16 100644 --- a/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/TestServlet.java +++ b/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/TestServlet.java @@ -46,6 +46,10 @@ public class TestServlet { @Remote(CacheSetup.MAGAZINE_CACHE) RemoteCache magazineCache; + @Inject + @Remote(CacheSetup.AUTHORS_CACHE) + RemoteCache authorsCache; + @Inject CounterManager counterManager; @@ -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(",", "[", "]")); + } } diff --git a/integration-tests/infinispan-client/src/test/java/io/quarkus/it/infinispan/client/InfinispanClientFunctionalityTest.java b/integration-tests/infinispan-client/src/test/java/io/quarkus/it/infinispan/client/InfinispanClientFunctionalityTest.java index c490f8d99ba63..9de116a8d3964 100644 --- a/integration-tests/infinispan-client/src/test/java/io/quarkus/it/infinispan/client/InfinispanClientFunctionalityTest.java +++ b/integration-tests/infinispan-client/src/test/java/io/quarkus/it/infinispan/client/InfinispanClientFunctionalityTest.java @@ -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]")); + } }