diff --git a/docs/src/main/asciidoc/infinispan-client-reference.adoc b/docs/src/main/asciidoc/infinispan-client-reference.adoc
index 49bdb980801b59..a6fcee3da0771b 100644
--- a/docs/src/main/asciidoc/infinispan-client-reference.adoc
+++ b/docs/src/main/asciidoc/infinispan-client-reference.adoc
@@ -115,6 +115,56 @@ quarkus.infinispan-client.client-intelligence=BASIC <2>
Use Infinispan Dev Services to run a server and connect without configuration.
====
+=== Configuring backup clusters in Cross-Site Replication
+In High Availability production deployments, it is common to have multiple Infinispan Clusters that are
+distributed across various Data Centers worldwide. Infinispan offers the capability to connect these clusters and
+configure backups between them. This enables seamless switching between clusters through both automated and manual
+methods using a single connection. To achieve this, it is necessary to configure the client to direct to the backup
+clusters.
+
+[source,properties]
+----
+quarkus.infinispan-client.hosts=host1:11222,host2:3122 <1>
+quarkus.infinispan-client.username=admin
+quarkus.infinispan-client.password=password
+quarkus.infinispan-client.backup-cluster.nyc-site.hosts=nyc1:11222,nyc2:21222,nyc3:31222 <2>
+quarkus.infinispan-client.backup-cluster.nyc-site.client-intelligence=BASIC <3>
+quarkus.infinispan-client.backup-cluster.lon-site.hosts=lon1:11222,lon2:21222,lon3:31222 <4>
+----
+<1> Sets Infinispan Server address list, separated with commas. This is the default cluster.
+<2> Configures a backup site 'nyc-site' with the provided address list
+<3> Configures a backup site 'nyc-site' with the provided client intelligence
+<4> Configures an additional backup site 'lon-site' with the provided address list
+
+Based on the provided configuration, in the event of the default cluster becoming unavailable, the client will
+seamlessly transition to one of the accessible backup clusters.
+Additionally, there is also the option to manually switch the client to an alternate cluster:
+
+[source,java]
+----
+@ApplicationScoped
+public class InfinispanExample {
+ @Inject
+ RemoteCacheManager cacheManager;
+
+ public void doSomething() {
+ cacheManager.switchToCluster("nyc-site"); //<1>
+ cacheManager.switchToCluster("lon-site"); //<2>
+ cacheManager.switchToDefaultCluster(); //<3>
+ }
+}
+----
+<1> The client connects to the 'nyc-site'.
+<2> The client connects to the 'lon-site'.
+<3> The client connects to the default site.
+
+[NOTE]
+====
+Cross-site replication is a powerful feature offered by Infinispan that facilitates data backup between clusters
+situated in geographically diverse data centers, even spanning across various cloud providers.
+Learn more in the link:https://infinispan.org/docs/stable/titles/xsite/xsite.html[Infinispan documentation].
+====
+
=== Default and named connections
This extension lets you configure a _default_ Infinispan client connections and _named_ ones.
Named connections are essential to connect to multiple Infinispan clusters.
@@ -163,7 +213,6 @@ the traces from the Infinispan Client to the Server.
This behavior can be disabled via the property `quarkus.infinispan-client.tracing.propagation.enabled`
=== 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:
diff --git a/extensions/infinispan-client/deployment/src/test/java/org/quarkus/infinispan/client/deployment/InfinispanConfigurationSetupTest.java b/extensions/infinispan-client/deployment/src/test/java/org/quarkus/infinispan/client/deployment/InfinispanConfigurationSetupTest.java
index c6aa1c49e57886..9cc7101a0da1b5 100644
--- a/extensions/infinispan-client/deployment/src/test/java/org/quarkus/infinispan/client/deployment/InfinispanConfigurationSetupTest.java
+++ b/extensions/infinispan-client/deployment/src/test/java/org/quarkus/infinispan/client/deployment/InfinispanConfigurationSetupTest.java
@@ -44,6 +44,11 @@ public void infinispanConnectionConfiguration() {
assertThat(configuration.security().ssl().provider()).isEqualTo("SSL_prov");
assertThat(configuration.security().ssl().protocol()).isEqualTo("SSL_protocol");
assertThat(configuration.security().ssl().ciphers()).containsExactlyInAnyOrder("SSL_cipher1", "SSL_cipher2");
+ assertThat(configuration.clusters().size()).isEqualTo(1);
+ assertThat(configuration.clusters().get(0).getClusterName()).isEqualTo("bsite");
+ assertThat(configuration.clusters().get(0).getCluster().get(0).host()).isEqualTo("bsite1");
+ assertThat(configuration.clusters().get(0).getCluster().get(0).port()).isEqualTo(32111);
+ assertThat(configuration.clusters().get(0).getClientIntelligence()).isEqualTo(ClientIntelligence.BASIC);
assertThat(configuration.remoteCaches().get("cache1").configuration()).isEqualTo("");
assertThat(configuration.remoteCaches().get("cache1").nearCacheBloomFilter()).isTrue();
diff --git a/extensions/infinispan-client/deployment/src/test/resources/cache-config-application.properties b/extensions/infinispan-client/deployment/src/test/resources/cache-config-application.properties
index 49238028c10100..54c95974449c33 100644
--- a/extensions/infinispan-client/deployment/src/test/resources/cache-config-application.properties
+++ b/extensions/infinispan-client/deployment/src/test/resources/cache-config-application.properties
@@ -16,6 +16,8 @@ quarkus.infinispan-client.trust-store-type=JCEKS
quarkus.infinispan-client.ssl-provider=SSL_prov
quarkus.infinispan-client.ssl-protocol=SSL_protocol
quarkus.infinispan-client.ssl-ciphers=SSL_cipher1,SSL_cipher2
+quarkus.infinispan-client.backup-cluster.bsite.hosts=bsite1:32111
+quarkus.infinispan-client.backup-cluster.bsite.client-intelligence=BASIC
# cache 1 config
quarkus.infinispan-client.cache.cache1.configuration=
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 d954202a327fc4..1b24793b36ccf0 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
@@ -20,6 +20,8 @@
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.RemoteCounterManagerFactory;
+import org.infinispan.client.hotrod.configuration.ClientIntelligence;
+import org.infinispan.client.hotrod.configuration.ClusterConfigurationBuilder;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.client.hotrod.impl.ConfigurationProperties;
import org.infinispan.client.hotrod.logging.Log;
@@ -289,6 +291,17 @@ private ConfigurationBuilder builderFromProperties(String infinispanClientName,
}
}
+ for (Map.Entry backupCluster : infinispanClientRuntimeConfig.backupCluster
+ .entrySet()) {
+ InfinispanClientRuntimeConfig.BackupClusterConfig backupClusterConfig = backupCluster.getValue();
+ ClusterConfigurationBuilder clusterConfigurationBuilder = builder.addCluster(backupCluster.getKey());
+ clusterConfigurationBuilder.addClusterNodes(backupClusterConfig.hosts);
+ if (backupClusterConfig.clientIntelligence.isPresent()) {
+ clusterConfigurationBuilder.clusterClientIntelligence(
+ ClientIntelligence.valueOf(backupClusterConfig.clientIntelligence.get()));
+ }
+ }
+
return builder;
}
diff --git a/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanClientRuntimeConfig.java b/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanClientRuntimeConfig.java
index 6884512349fff3..157db0dd724a69 100644
--- a/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanClientRuntimeConfig.java
+++ b/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanClientRuntimeConfig.java
@@ -180,6 +180,19 @@ public class InfinispanClientRuntimeConfig {
@ConfigItem
public Map cache = new HashMap<>();
+ /**
+ * // @formatter:off
+ * Configures cross site replication from the client with the provided configuration.
+ * This allows automatic failover when a site is down, as well as switching manual with methods like:
+ *
+ * cacheManager.switchToDefaultCluster();
+ * cacheManager.switchToCluster('clusterName')
+ *
+ * // @formatter:on
+ */
+ @ConfigItem
+ public Map backupCluster = new HashMap<>();
+
@ConfigGroup
public static class RemoteCacheConfig {
@@ -233,6 +246,33 @@ public static class RemoteCacheConfig {
public Optional nearCacheUseBloomFilter;
}
+ @ConfigGroup
+ public static class BackupClusterConfig {
+ // @formatter:off
+ /**
+ * Sets the host name/port to connect to. Each one is separated by a semicolon (eg. hostA:11222;hostB:11222).
+ */
+ // @formatter:on
+ @ConfigItem
+ public String hosts;
+
+ // @formatter:off
+ /**
+ * Sets client intelligence used by authentication
+ * Available values:
+ * * `BASIC` - Means that the client doesn't handle server topology changes and therefore will only use the list
+ * of servers supplied at configuration time.
+ * * `TOPOLOGY_AWARE` - Use this provider if you don't want the client to present any certificates to the
+ * remote TLS host.
+ * * `HASH_DISTRIBUTION_AWARE` - Like `TOPOLOGY_AWARE` but with the additional advantage that each request
+ * involving keys will be routed to the server who is the primary owner which improves performance
+ * greatly. This is the default.
+ */
+ // @formatter:on
+ @ConfigItem(defaultValue = "HASH_DISTRIBUTION_AWARE")
+ Optional clientIntelligence;
+ }
+
@Override
public String toString() {
return "InfinispanClientRuntimeConfig{" +