You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sa...@apache.org on 2018/09/01 20:47:57 UTC
cassandra git commit: Make AuthCache easier to subclass
Repository: cassandra
Updated Branches:
refs/heads/trunk cc12665bb -> 960174da6
Make AuthCache easier to subclass
Patch by Kurt Greaves; reviewed by Sam Tunnicliffe for CASSANDRA-14662
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/960174da
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/960174da
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/960174da
Branch: refs/heads/trunk
Commit: 960174da67eb6008c73340e61700ea34ec550a12
Parents: cc12665
Author: kurt <ku...@instaclustr.com>
Authored: Sat Sep 1 21:43:58 2018 +0100
Committer: Sam Tunnicliffe <sa...@beobal.com>
Committed: Sat Sep 1 21:46:11 2018 +0100
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../org/apache/cassandra/auth/AuthCache.java | 115 +++++++++++-----
.../apache/cassandra/auth/AuthCacheMBean.java | 4 +-
.../apache/cassandra/auth/NetworkAuthCache.java | 2 +-
.../cassandra/auth/PasswordAuthenticator.java | 2 +-
.../apache/cassandra/auth/PermissionsCache.java | 2 +-
.../cassandra/auth/PermissionsCacheMBean.java | 26 ----
.../org/apache/cassandra/auth/RolesCache.java | 2 +-
.../apache/cassandra/auth/RolesCacheMBean.java | 26 ----
.../apache/cassandra/auth/AuthCacheTest.java | 137 +++++++++++++++++++
10 files changed, 229 insertions(+), 88 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/960174da/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index a7468f4..aca31fe 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
4.0
+ * Make AuthCache more easily extendable (CASSANDRA-14662)
* Extend RolesCache to include detailed role info (CASSANDRA-14497)
* Add fqltool compare (CASSANDRA-14619)
* Add fqltool replay (CASSANDRA-14618)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/960174da/src/java/org/apache/cassandra/auth/AuthCache.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/AuthCache.java b/src/java/org/apache/cassandra/auth/AuthCache.java
index d6ff0b0..4f36a63 100644
--- a/src/java/org/apache/cassandra/auth/AuthCache.java
+++ b/src/java/org/apache/cassandra/auth/AuthCache.java
@@ -35,24 +35,40 @@ import com.github.benmanes.caffeine.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class AuthCache<K, V> implements AuthCacheMBean
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class AuthCache<K, V> implements AuthCacheMBean<K>
{
private static final Logger logger = LoggerFactory.getLogger(AuthCache.class);
private static final String MBEAN_NAME_BASE = "org.apache.cassandra.auth:type=";
- private volatile LoadingCache<K, V> cache;
-
- private final String name;
- private final IntConsumer setValidityDelegate;
- private final IntSupplier getValidityDelegate;
- private final IntConsumer setUpdateIntervalDelegate;
- private final IntSupplier getUpdateIntervalDelegate;
- private final IntConsumer setMaxEntriesDelegate;
- private final IntSupplier getMaxEntriesDelegate;
- private final Function<K, V> loadFunction;
- private final BooleanSupplier enableCache;
-
+ /**
+ * Underlying cache. LoadingCache will call underlying load function on {@link #get} if key is not present
+ */
+ protected volatile LoadingCache<K, V> cache;
+
+ private String name;
+ private IntConsumer setValidityDelegate;
+ private IntSupplier getValidityDelegate;
+ private IntConsumer setUpdateIntervalDelegate;
+ private IntSupplier getUpdateIntervalDelegate;
+ private IntConsumer setMaxEntriesDelegate;
+ private IntSupplier getMaxEntriesDelegate;
+ private Function<K, V> loadFunction;
+ private BooleanSupplier enableCache;
+
+ /**
+ * @param name Used for MBean
+ * @param setValidityDelegate Used to set cache validity period. See {@link Policy#expireAfterWrite()}
+ * @param getValidityDelegate Getter for validity period
+ * @param setUpdateIntervalDelegate Used to set cache update interval. See {@link Policy#refreshAfterWrite()}
+ * @param getUpdateIntervalDelegate Getter for update interval
+ * @param setMaxEntriesDelegate Used to set max # entries in cache. See {@link com.github.benmanes.caffeine.cache.Policy.Eviction#setMaximum(long)}
+ * @param getMaxEntriesDelegate Getter for max entries.
+ * @param loadFunction Function to load the cache. Called on {@link #get(Object)}
+ * @param cacheEnabledDelegate Used to determine if cache is enabled.
+ */
protected AuthCache(String name,
IntConsumer setValidityDelegate,
IntSupplier getValidityDelegate,
@@ -61,23 +77,26 @@ public class AuthCache<K, V> implements AuthCacheMBean
IntConsumer setMaxEntriesDelegate,
IntSupplier getMaxEntriesDelegate,
Function<K, V> loadFunction,
- BooleanSupplier enableCache)
+ BooleanSupplier cacheEnabledDelegate)
{
- this.name = name;
- this.setValidityDelegate = setValidityDelegate;
- this.getValidityDelegate = getValidityDelegate;
- this.setUpdateIntervalDelegate = setUpdateIntervalDelegate;
- this.getUpdateIntervalDelegate = getUpdateIntervalDelegate;
- this.setMaxEntriesDelegate = setMaxEntriesDelegate;
- this.getMaxEntriesDelegate = getMaxEntriesDelegate;
- this.loadFunction = loadFunction;
- this.enableCache = enableCache;
+ this.name = checkNotNull(name);
+ this.setValidityDelegate = checkNotNull(setValidityDelegate);
+ this.getValidityDelegate = checkNotNull(getValidityDelegate);
+ this.setUpdateIntervalDelegate = checkNotNull(setUpdateIntervalDelegate);
+ this.getUpdateIntervalDelegate = checkNotNull(getUpdateIntervalDelegate);
+ this.setMaxEntriesDelegate = checkNotNull(setMaxEntriesDelegate);
+ this.getMaxEntriesDelegate = checkNotNull(getMaxEntriesDelegate);
+ this.loadFunction = checkNotNull(loadFunction);
+ this.enableCache = checkNotNull(cacheEnabledDelegate);
init();
}
+ /**
+ * Do setup for the cache and MBean.
+ */
protected void init()
{
- this.cache = initCache(null);
+ cache = initCache(null);
try
{
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
@@ -107,6 +126,14 @@ public class AuthCache<K, V> implements AuthCacheMBean
return new ObjectName(MBEAN_NAME_BASE + name);
}
+ /**
+ * Retrieve a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+ * "load" the value if it's not present, thus populating the key.
+ * @param k
+ * @return The current value of {@code K} if cached or loaded.
+ *
+ * See {@link LoadingCache#get(Object)} for possible exceptions.
+ */
public V get(K k)
{
if (cache == null)
@@ -115,17 +142,28 @@ public class AuthCache<K, V> implements AuthCacheMBean
return cache.get(k);
}
+ /**
+ * Invalidate the entire cache.
+ */
public void invalidate()
{
cache = initCache(null);
}
+ /**
+ * Invalidate a key
+ * @param k key to invalidate
+ */
public void invalidate(K k)
{
if (cache != null)
cache.invalidate(k);
}
+ /**
+ * Time in milliseconds that a value in the cache will expire after.
+ * @param validityPeriod in milliseconds
+ */
public void setValidity(int validityPeriod)
{
if (Boolean.getBoolean("cassandra.disable_auth_caches_remote_configuration"))
@@ -140,6 +178,10 @@ public class AuthCache<K, V> implements AuthCacheMBean
return getValidityDelegate.getAsInt();
}
+ /**
+ * Time in milliseconds after which an entry in the cache should be refreshed (it's load function called again)
+ * @param updateInterval in milliseconds
+ */
public void setUpdateInterval(int updateInterval)
{
if (Boolean.getBoolean("cassandra.disable_auth_caches_remote_configuration"))
@@ -154,6 +196,10 @@ public class AuthCache<K, V> implements AuthCacheMBean
return getUpdateIntervalDelegate.getAsInt();
}
+ /**
+ * Set maximum number of entries in the cache.
+ * @param maxEntries
+ */
public void setMaxEntries(int maxEntries)
{
if (Boolean.getBoolean("cassandra.disable_auth_caches_remote_configuration"))
@@ -168,7 +214,14 @@ public class AuthCache<K, V> implements AuthCacheMBean
return getMaxEntriesDelegate.getAsInt();
}
- private LoadingCache<K, V> initCache(LoadingCache<K, V> existing)
+ /**
+ * (Re-)initialise the underlying cache. Will update validity, max entries, and update interval if
+ * any have changed. The underlying {@link LoadingCache} will be initiated based on the provided {@code loadFunction}.
+ * Note: If you need some unhandled cache setting to be set you should extend {@link AuthCache} and override this method.
+ * @param existing If not null will only update cache update validity, max entries, and update interval.
+ * @return New {@link LoadingCache} if existing was null, otherwise the existing {@code cache}
+ */
+ protected LoadingCache<K, V> initCache(LoadingCache<K, V> existing)
{
if (!enableCache.getAsBoolean())
return null;
@@ -181,14 +234,14 @@ public class AuthCache<K, V> implements AuthCacheMBean
if (existing == null) {
return Caffeine.newBuilder()
- .refreshAfterWrite(getUpdateInterval(), TimeUnit.MILLISECONDS)
- .expireAfterWrite(getValidity(), TimeUnit.MILLISECONDS)
- .maximumSize(getMaxEntries())
- .executor(MoreExecutors.directExecutor())
- .build(loadFunction::apply);
+ .refreshAfterWrite(getUpdateInterval(), TimeUnit.MILLISECONDS)
+ .expireAfterWrite(getValidity(), TimeUnit.MILLISECONDS)
+ .maximumSize(getMaxEntries())
+ .executor(MoreExecutors.directExecutor())
+ .build(loadFunction::apply);
}
- // Always set as manditory
+ // Always set as mandatory
cache.policy().refreshAfterWrite().ifPresent(policy ->
policy.setExpiresAfter(getUpdateInterval(), TimeUnit.MILLISECONDS));
cache.policy().expireAfterWrite().ifPresent(policy ->
http://git-wip-us.apache.org/repos/asf/cassandra/blob/960174da/src/java/org/apache/cassandra/auth/AuthCacheMBean.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/AuthCacheMBean.java b/src/java/org/apache/cassandra/auth/AuthCacheMBean.java
index 43fb88e..1416044 100644
--- a/src/java/org/apache/cassandra/auth/AuthCacheMBean.java
+++ b/src/java/org/apache/cassandra/auth/AuthCacheMBean.java
@@ -18,10 +18,12 @@
package org.apache.cassandra.auth;
-public interface AuthCacheMBean
+public interface AuthCacheMBean<T>
{
public void invalidate();
+ public void invalidate(T t);
+
public void setValidity(int validityPeriod);
public int getValidity();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/960174da/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/NetworkAuthCache.java b/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
index 15b1819..0991889 100644
--- a/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
+++ b/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
@@ -20,7 +20,7 @@ package org.apache.cassandra.auth;
import org.apache.cassandra.config.DatabaseDescriptor;
-public class NetworkAuthCache extends AuthCache<RoleResource, DCPermissions> implements AuthCacheMBean
+public class NetworkAuthCache extends AuthCache<RoleResource, DCPermissions>
{
public NetworkAuthCache(INetworkAuthorizer authorizer)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/960174da/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
index 27a68a0..b10136e 100644
--- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
@@ -228,7 +228,7 @@ public class PasswordAuthenticator implements IAuthenticator
}
}
- private static class CredentialsCache extends AuthCache<String, String> implements CredentialsCacheMBean
+ private static class CredentialsCache extends AuthCache<String, String>
{
private CredentialsCache(PasswordAuthenticator authenticator)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/960174da/src/java/org/apache/cassandra/auth/PermissionsCache.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/PermissionsCache.java b/src/java/org/apache/cassandra/auth/PermissionsCache.java
index 981ede8..a33f5d1 100644
--- a/src/java/org/apache/cassandra/auth/PermissionsCache.java
+++ b/src/java/org/apache/cassandra/auth/PermissionsCache.java
@@ -22,7 +22,7 @@ import java.util.Set;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.utils.Pair;
-public class PermissionsCache extends AuthCache<Pair<AuthenticatedUser, IResource>, Set<Permission>> implements PermissionsCacheMBean
+public class PermissionsCache extends AuthCache<Pair<AuthenticatedUser, IResource>, Set<Permission>>
{
public PermissionsCache(IAuthorizer authorizer)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/960174da/src/java/org/apache/cassandra/auth/PermissionsCacheMBean.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/PermissionsCacheMBean.java b/src/java/org/apache/cassandra/auth/PermissionsCacheMBean.java
deleted file mode 100644
index d370d06..0000000
--- a/src/java/org/apache/cassandra/auth/PermissionsCacheMBean.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.cassandra.auth;
-
-/**
- * Retained since CASSANDRA-7715 for backwards compatibility of MBean interface
- * classes. This should be removed in the next major version (4.0)
- */
-public interface PermissionsCacheMBean extends AuthCacheMBean
-{
-}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/960174da/src/java/org/apache/cassandra/auth/RolesCache.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/RolesCache.java b/src/java/org/apache/cassandra/auth/RolesCache.java
index cc178ce..d01de63 100644
--- a/src/java/org/apache/cassandra/auth/RolesCache.java
+++ b/src/java/org/apache/cassandra/auth/RolesCache.java
@@ -23,7 +23,7 @@ import java.util.stream.Collectors;
import org.apache.cassandra.config.DatabaseDescriptor;
-public class RolesCache extends AuthCache<RoleResource, Set<Role>> implements RolesCacheMBean
+public class RolesCache extends AuthCache<RoleResource, Set<Role>>
{
public RolesCache(IRoleManager roleManager, BooleanSupplier enableCache)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/960174da/src/java/org/apache/cassandra/auth/RolesCacheMBean.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/RolesCacheMBean.java b/src/java/org/apache/cassandra/auth/RolesCacheMBean.java
deleted file mode 100644
index 06482d7..0000000
--- a/src/java/org/apache/cassandra/auth/RolesCacheMBean.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.cassandra.auth;
-
-/**
- * Retained since CASSANDRA-7715 for backwards compatibility of MBean interface
- * classes. This should be removed in the next major version (4.0)
- */
-public interface RolesCacheMBean extends AuthCacheMBean
-{
-}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/960174da/test/unit/org/apache/cassandra/auth/AuthCacheTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/auth/AuthCacheTest.java b/test/unit/org/apache/cassandra/auth/AuthCacheTest.java
new file mode 100644
index 0000000..cc78ebc
--- /dev/null
+++ b/test/unit/org/apache/cassandra/auth/AuthCacheTest.java
@@ -0,0 +1,137 @@
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.auth;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.config.DatabaseDescriptor;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class AuthCacheTest
+{
+ private boolean loadFuncCalled = false;
+ private boolean isCacheEnabled = false;
+
+ @BeforeClass
+ public static void setup()
+ {
+ DatabaseDescriptor.daemonInitialization();
+ }
+
+ @Test
+ public void testCaching()
+ {
+ AuthCache<String, String> authCache = new AuthCache<>("TestCache",
+ DatabaseDescriptor::setCredentialsValidity,
+ DatabaseDescriptor::getCredentialsValidity,
+ DatabaseDescriptor::setCredentialsUpdateInterval,
+ DatabaseDescriptor::getCredentialsUpdateInterval,
+ DatabaseDescriptor::setCredentialsCacheMaxEntries,
+ DatabaseDescriptor::getCredentialsCacheMaxEntries,
+ this::load,
+ () -> true
+ );
+
+ // Test cacheloader is called if set
+ loadFuncCalled = false;
+ String result = authCache.get("test");
+ assertTrue(loadFuncCalled);
+ Assert.assertEquals("load", result);
+
+ // value should be fetched from cache
+ loadFuncCalled = false;
+ String result2 = authCache.get("test");
+ assertFalse(loadFuncCalled);
+ Assert.assertEquals("load", result2);
+
+ // value should be fetched from cache after complete invalidate
+ authCache.invalidate();
+ loadFuncCalled = false;
+ String result3 = authCache.get("test");
+ assertTrue(loadFuncCalled);
+ Assert.assertEquals("load", result3);
+
+ // value should be fetched from cache after invalidating key
+ authCache.invalidate("test");
+ loadFuncCalled = false;
+ String result4 = authCache.get("test");
+ assertTrue(loadFuncCalled);
+ Assert.assertEquals("load", result4);
+
+ // set cache to null and load function should be called
+ loadFuncCalled = false;
+ authCache.cache = null;
+ String result5 = authCache.get("test");
+ assertTrue(loadFuncCalled);
+ Assert.assertEquals("load", result5);
+ }
+
+ @Test
+ public void testInitCache()
+ {
+ // Test that a validity of <= 0 will turn off caching
+ DatabaseDescriptor.setCredentialsValidity(0);
+ AuthCache<String, String> authCache = new AuthCache<>("TestCache2",
+ DatabaseDescriptor::setCredentialsValidity,
+ DatabaseDescriptor::getCredentialsValidity,
+ DatabaseDescriptor::setCredentialsUpdateInterval,
+ DatabaseDescriptor::getCredentialsUpdateInterval,
+ DatabaseDescriptor::setCredentialsCacheMaxEntries,
+ DatabaseDescriptor::getCredentialsCacheMaxEntries,
+ this::load,
+ () -> true);
+ assertNull(authCache.cache);
+ authCache.setValidity(2000);
+ authCache.cache = authCache.initCache(null);
+ assertNotNull(authCache.cache);
+
+ // Test enableCache works as intended
+ authCache = new AuthCache<>("TestCache3",
+ DatabaseDescriptor::setCredentialsValidity,
+ DatabaseDescriptor::getCredentialsValidity,
+ DatabaseDescriptor::setCredentialsUpdateInterval,
+ DatabaseDescriptor::getCredentialsUpdateInterval,
+ DatabaseDescriptor::setCredentialsCacheMaxEntries,
+ DatabaseDescriptor::getCredentialsCacheMaxEntries,
+ this::load,
+ () -> isCacheEnabled);
+ assertNull(authCache.cache);
+ isCacheEnabled = true;
+ authCache.cache = authCache.initCache(null);
+ assertNotNull(authCache.cache);
+
+ // Ensure at a minimum these policies have been initialised by default
+ assertTrue(authCache.cache.policy().expireAfterWrite().isPresent());
+ assertTrue(authCache.cache.policy().refreshAfterWrite().isPresent());
+ assertTrue(authCache.cache.policy().eviction().isPresent());
+ }
+
+ private String load(String test)
+ {
+ loadFuncCalled = true;
+ return "load";
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org