Skip to content

Commit

Permalink
Test delete entry in remote Infinispan and check removals are replica…
Browse files Browse the repository at this point in the history
…ted to the second site (#823)

Signed-off-by: Michal Hajas <[email protected]>
Signed-off-by: Alexander Schwartz <[email protected]>
Co-authored-by: Alexander Schwartz <[email protected]>
  • Loading branch information
mhajas and ahus1 committed May 22, 2024
1 parent 99cfbda commit 07e7182
Show file tree
Hide file tree
Showing 11 changed files with 414 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,64 +18,82 @@

package org.keycloak.benchmark.cache;

import java.util.UUID;

import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;

import org.infinispan.Cache;
import org.infinispan.commons.CacheConfigurationException;
import jakarta.ws.rs.QueryParam;
import org.infinispan.commons.api.BasicCache;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.benchmark.dataset.TaskResponse;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.utils.MediaType;

import java.util.UUID;

/**
* @author <a href="mailto:[email protected]">Marek Posolda</a>
*/
public class CacheResource {
public abstract class CacheResource {

protected static final Logger logger = Logger.getLogger(CacheResource.class);

private final Cache<Object, Object> cache;

public CacheResource(KeycloakSession session, String cacheName) {
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
try {
this.cache = provider.getCache(cacheName);
} catch (CacheConfigurationException cce) {
logger.error(cce.getMessage());
throw new NotFoundException("Cache does not exists");
}
}
public abstract BasicCache<Object, Object> getCache();


@GET
@Path("/clear")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public TaskResponse clear() {
cache.clear();
logger.infof("Cache %s cleared successfully", cache.getName());
return TaskResponse.statusMessage("Cache " + cache.getName() + " cleared successfully");
getCache().clear();
logger.infof("Cache %s cleared successfully", getCache().getName());
return TaskResponse.statusMessage("Cache " + getCache().getName() + " cleared successfully");
}

@GET
@Path("/contains/{id}")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public boolean contains(@PathParam("id") String id) {
if (cache.containsKey(id)) {
if (getCache().containsKey(id)) {
return true;
} else if (id.length() == 36) {
try {
UUID uuid = UUID.fromString(id);
return getCache().containsKey(uuid);
} catch (IllegalArgumentException iae) {
logger.warnf("Given string %s not an UUID", id);
return false;
}
} else {
return false;
}
}

@GET
@Path("/size")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public int size() {
return getCache().size();
}

protected abstract BasicCache<Object, Object> decorateCacheForRemovalAndSkipListenersIfTrue(boolean skipListeners);

@GET
@Path("/remove/{id}")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public boolean remove(@PathParam("id") String id, @QueryParam("skipListeners") @DefaultValue("false") boolean skipListeners) {
if (decorateCacheForRemovalAndSkipListenersIfTrue(skipListeners).remove(id) != null) {
return true;
} else if (id.length() == 36) {
try {
UUID uuid = UUID.fromString(id);
return cache.containsKey(uuid);
return decorateCacheForRemovalAndSkipListenersIfTrue(skipListeners).remove(uuid) != null;
} catch (IllegalArgumentException iae) {
logger.warnf("Given string %s not an UUID", id);
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import org.infinispan.Cache;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.context.Flag;
import org.infinispan.manager.EmbeddedCacheManager;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.benchmark.dataset.TaskResponse;
Expand Down Expand Up @@ -112,7 +113,7 @@ public TaskResponse clearSessions() {

@Path("/{cache}")
public CacheResource getCacheResource(@PathParam("cache") String cacheName) {
return new CacheResource(session, cacheName);
return new EmbeddedCacheResource(session, cacheName);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.keycloak.benchmark.cache;

import jakarta.ws.rs.NotFoundException;
import org.infinispan.Cache;
import org.infinispan.commons.CacheConfigurationException;
import org.infinispan.commons.api.BasicCache;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession;

public class EmbeddedCacheResource extends CacheResource {

private final Cache<Object, Object> cache;

public EmbeddedCacheResource(KeycloakSession session, String cacheName) {
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
try {
this.cache = provider.getCache(cacheName);
} catch (CacheConfigurationException cce) {
logger.error(cce.getMessage());
throw new NotFoundException("Cache does not exists");
}
}

@Override
public BasicCache<Object, Object> getCache() {
return cache;
}

@Override
protected BasicCache<Object, Object> decorateCacheForRemovalAndSkipListenersIfTrue(boolean skipListeners) {
return skipListeners
? cache.getAdvancedCache().withFlags(org.infinispan.context.Flag.SKIP_CACHE_STORE)
: cache.getAdvancedCache();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,21 @@

package org.keycloak.benchmark.cache;

import java.util.UUID;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;

import org.infinispan.Cache;
import org.infinispan.client.hotrod.Flag;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.commons.CacheConfigurationException;
import org.infinispan.commons.api.BasicCache;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.benchmark.dataset.TaskResponse;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.connections.infinispan.InfinispanUtil;
import org.keycloak.models.KeycloakSession;
import org.keycloak.utils.MediaType;

/**
* @author <a href="mailto:[email protected]">Marek Posolda</a>
*/
public class RemoteCacheResource {
public class RemoteCacheResource extends CacheResource {

protected static final Logger logger = Logger.getLogger(RemoteCacheResource.class);

Expand All @@ -65,33 +57,15 @@ public RemoteCacheResource(KeycloakSession session, String cacheName) {
}


@GET
@Path("/clear")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public TaskResponse clear() {
remoteCache.clear();
logger.infof("Remote cache %s cleared successfully", remoteCache.getName());
return TaskResponse.statusMessage("Remote cache " + remoteCache.getName() + " cleared successfully");
@Override
public BasicCache<Object, Object> getCache() {
return remoteCache;
}

@GET
@Path("/contains/{id}")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public boolean contains(@PathParam("id") String id) {
if (remoteCache.containsKey(id)) {
return true;
} else if (id.length() == 36) {
try {
UUID uuid = UUID.fromString(id);
return remoteCache.containsKey(uuid);
} catch (IllegalArgumentException iae) {
logger.warnf("Given string %s not an UUID", id);
return false;
}
} else {
return false;
}
@Override
protected BasicCache<Object, Object> decorateCacheForRemovalAndSkipListenersIfTrue(boolean skipListeners) {
return skipListeners
? remoteCache.withFlags(Flag.SKIP_LISTENER_NOTIFICATION, Flag.FORCE_RETURN_VALUE)
: remoteCache.withFlags(Flag.FORCE_RETURN_VALUE);
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
package org.keycloak.benchmark.crossdc;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.keycloak.benchmark.crossdc.util.HttpClientUtils.MOCK_COOKIE_MANAGER;
import static org.keycloak.benchmark.crossdc.util.InfinispanUtils.DISTRIBUTED_CACHES;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.net.http.HttpClient;
import java.util.Collections;
import java.util.List;

import jakarta.ws.rs.NotFoundException;
import org.jboss.logging.Logger;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
Expand All @@ -28,8 +17,6 @@
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;

import jakarta.ws.rs.NotFoundException;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
Expand All @@ -41,14 +28,16 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.keycloak.benchmark.crossdc.util.HttpClientUtils.MOCK_COOKIE_MANAGER;
import static org.keycloak.benchmark.crossdc.util.InfinispanUtils.CLIENT_SESSIONS;
import static org.keycloak.benchmark.crossdc.util.InfinispanUtils.DISTRIBUTED_CACHES;
import static org.keycloak.benchmark.crossdc.util.InfinispanUtils.SESSIONS;

public abstract class AbstractCrossDCTest {
private static final Logger LOG = Logger.getLogger(AbstractCrossDCTest.class);
protected static HttpClient HTTP_CLIENT = HttpClientUtils.newHttpClient();
protected static final DatacenterInfo DC_1, DC_2;
protected static final KeycloakClient LOAD_BALANCER_KEYCLOAK;
public static String ISPN_USERNAME = System.getProperty("infinispan.username", "developer");;
public static String ISPN_USERNAME = System.getProperty("infinispan.username", "developer");
public static final String REALM_NAME = "cross-dc-test-realm";
public static final String CLIENTID = "cross-dc-test-client";
public static final String CLIENT_SECRET = "cross-dc-test-client-secret";
Expand Down Expand Up @@ -119,6 +108,22 @@ public void setUpTestEnvironment() throws UnknownHostException {
user.setCredentials(Collections.singletonList(credential));

realmResource.users().create(user).close();

clearCache(DC_1, SESSIONS);
clearCache(DC_1, CLIENT_SESSIONS);
clearCache(DC_2, SESSIONS);
clearCache(DC_2, CLIENT_SESSIONS);
assertCacheSize(SESSIONS, 0);
assertCacheSize(CLIENT_SESSIONS, 0);
}

private void clearCache(DatacenterInfo dc, String cache) {
dc.kc().embeddedIspn().cache(cache).clear();
dc.ispn().cache(cache).clear();
if (cache.equals(SESSIONS) || cache.equals(CLIENT_SESSIONS)) {
// those sessions will have been invalidated
KeycloakClient.cleanAdminClients();
}
}

@AfterEach
Expand Down Expand Up @@ -161,11 +166,11 @@ private static void failbackHealthChecks() throws URISyntaxException, IOExceptio

protected void assertCacheSize(String cache, int size) {
// Embedded caches
assertEquals(DC_1.kc().embeddedIspn().cache(cache).size(), size, () -> "Embedded cache " + cache + " in DC1 has " + DC_1.ispn().cache(cache).size() + " entries");
assertEquals(DC_2.kc().embeddedIspn().cache(cache).size(), size, () -> "Embedded cache " + cache + " in DC2 has " + DC_2.ispn().cache(cache).size() + " entries");
assertEquals(size, DC_1.kc().embeddedIspn().cache(cache).size(), () -> "Embedded cache " + cache + " in DC1 has " + DC_1.ispn().cache(cache).size() + " entries");
assertEquals(size, DC_2.kc().embeddedIspn().cache(cache).size(), () -> "Embedded cache " + cache + " in DC2 has " + DC_2.ispn().cache(cache).size() + " entries");

// External caches
assertEquals(DC_1.ispn().cache(cache).size(), size, () -> "External cache " + cache + " in DC1 has " + DC_1.ispn().cache(cache).size() + " entries");
assertEquals(DC_2.ispn().cache(cache).size(), size, () -> "External cache " + cache + " in DC2 has " + DC_2.ispn().cache(cache).size() + " entries");
assertEquals(size, DC_1.ispn().cache(cache).size(), () -> "External cache " + cache + " in DC1 has " + DC_1.ispn().cache(cache).size() + " entries");
assertEquals(size, DC_2.ispn().cache(cache).size(), () -> "External cache " + cache + " in DC2 has " + DC_2.ispn().cache(cache).size() + " entries");
}
}
Loading

0 comments on commit 07e7182

Please sign in to comment.