You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by al...@apache.org on 2015/01/09 02:03:48 UTC
cassandra git commit: Introduce background cache refreshing to
permissions cache
Repository: cassandra
Updated Branches:
refs/heads/cassandra-2.0 bd3c47ca7 -> e750ab238
Introduce background cache refreshing to permissions cache
patch by Sam Tunnicliffe; reviewed by Aleksey Yeschenko for
CASSANDRA-8194
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/e750ab23
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e750ab23
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e750ab23
Branch: refs/heads/cassandra-2.0
Commit: e750ab238e07daa61180d2451ba90f819a4cf5a1
Parents: bd3c47c
Author: Sam Tunnicliffe <sa...@beobal.com>
Authored: Fri Jan 9 04:02:32 2015 +0300
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Fri Jan 9 04:02:32 2015 +0300
----------------------------------------------------------------------
CHANGES.txt | 2 +
conf/cassandra.yaml | 8 ++
src/java/org/apache/cassandra/auth/Auth.java | 55 ++--------
.../org/apache/cassandra/auth/AuthMBean.java | 27 -----
.../apache/cassandra/auth/PermissionsCache.java | 108 +++++++++++++++++++
.../org/apache/cassandra/config/Config.java | 2 +
.../cassandra/config/DatabaseDescriptor.java | 10 +-
.../apache/cassandra/service/ClientState.java | 15 +--
8 files changed, 138 insertions(+), 89 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e750ab23/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 9ccbf45..adb374a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,6 @@
2.0.12:
+ * Introduce background cache refreshing to permissions cache
+ (CASSANDRA-8194)
* Fix race condition in StreamTransferTask that could lead to
infinite loops and premature sstable deletion (CASSANDRA-7704)
* Add an extra version check to MigrationTask (CASSANDRA-8462)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e750ab23/conf/cassandra.yaml
----------------------------------------------------------------------
diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml
index 5eaffc2..45290aa 100644
--- a/conf/cassandra.yaml
+++ b/conf/cassandra.yaml
@@ -79,6 +79,14 @@ authorizer: AllowAllAuthorizer
# Will be disabled automatically for AllowAllAuthorizer.
permissions_validity_in_ms: 2000
+# Refresh interval for permissions cache (if enabled).
+# After this interval, cache entries become eligible for refresh. Upon next
+# access, an async reload is scheduled and the old value returned until it
+# completes. If permissions_validity_in_ms is non-zero, then this must be
+# also.
+# Defaults to the same value as permissions_validity_in_ms.
+# permissions_update_interval_in_ms: 1000
+
# The partitioner is responsible for distributing groups of rows (by
# partition key) across nodes in the cluster. You should leave this
# alone for new clusters. The partitioner can NOT be changed without
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e750ab23/src/java/org/apache/cassandra/auth/Auth.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/Auth.java b/src/java/org/apache/cassandra/auth/Auth.java
index 94d4b3d..465643d 100644
--- a/src/java/org/apache/cassandra/auth/Auth.java
+++ b/src/java/org/apache/cassandra/auth/Auth.java
@@ -20,9 +20,6 @@ package org.apache.cassandra.auth;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
@@ -32,9 +29,9 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.KSMetaData;
import org.apache.cassandra.config.Schema;
-import org.apache.cassandra.cql3.UntypedResultSet;
-import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.cql3.statements.SelectStatement;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.exceptions.RequestExecutionException;
@@ -43,9 +40,8 @@ import org.apache.cassandra.locator.SimpleStrategy;
import org.apache.cassandra.service.*;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
-import org.apache.cassandra.utils.Pair;
-public class Auth implements AuthMBean
+public class Auth
{
private static final Logger logger = LoggerFactory.getLogger(Auth.class);
@@ -57,8 +53,10 @@ public class Auth implements AuthMBean
public static final String USERS_CF = "users";
// User-level permissions cache.
- public static volatile LoadingCache<Pair<AuthenticatedUser, IResource>, Set<Permission>> permissionsCache = initPermissionsCache(null);
-
+ private static final PermissionsCache permissionsCache = new PermissionsCache(DatabaseDescriptor.getPermissionsValidity(),
+ DatabaseDescriptor.getPermissionsUpdateInterval(),
+ DatabaseDescriptor.getPermissionsCacheMaxEntries(),
+ DatabaseDescriptor.getAuthorizer());
private static final String USERS_CF_SCHEMA = String.format("CREATE TABLE %s.%s ("
+ "name text,"
@@ -71,44 +69,9 @@ public class Auth implements AuthMBean
private static SelectStatement selectUserStatement;
- public int getPermissionsValidity()
- {
- return DatabaseDescriptor.getPermissionsValidity();
- }
-
- public void setPermissionsValidity(int timeoutInMs)
- {
- DatabaseDescriptor.setPermissionsValidity(timeoutInMs);
- permissionsCache = initPermissionsCache(permissionsCache);
- }
-
- public void invalidatePermissionsCache()
- {
- permissionsCache = initPermissionsCache(null);
- }
-
- private static LoadingCache<Pair<AuthenticatedUser, IResource>, Set<Permission>> initPermissionsCache(LoadingCache<Pair<AuthenticatedUser, IResource>, Set<Permission>> oldCache)
+ public static Set<Permission> getPermissions(AuthenticatedUser user, IResource resource)
{
- if (DatabaseDescriptor.getAuthorizer() instanceof AllowAllAuthorizer)
- return null;
-
- int validityPeriod = DatabaseDescriptor.getPermissionsValidity();
- if (validityPeriod <= 0)
- return null;
-
- LoadingCache<Pair<AuthenticatedUser, IResource>, Set<Permission>> newCache =
- CacheBuilder.newBuilder().expireAfterWrite(validityPeriod, TimeUnit.MILLISECONDS)
- .build(new CacheLoader<Pair<AuthenticatedUser, IResource>, Set<Permission>>()
- {
- public Set<Permission> load(Pair<AuthenticatedUser, IResource> userResource)
- {
- return DatabaseDescriptor.getAuthorizer().authorize(userResource.left,
- userResource.right);
- }
- });
- if (oldCache != null)
- newCache.putAll(oldCache.asMap());
- return newCache;
+ return permissionsCache.getPermissions(user, resource);
}
/**
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e750ab23/src/java/org/apache/cassandra/auth/AuthMBean.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/AuthMBean.java b/src/java/org/apache/cassandra/auth/AuthMBean.java
deleted file mode 100644
index db11f21..0000000
--- a/src/java/org/apache/cassandra/auth/AuthMBean.java
+++ /dev/null
@@ -1,27 +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;
-
-public interface AuthMBean
-{
- public int getPermissionsValidity();
-
- public void setPermissionsValidity(int timeoutInMs);
-
- public void invalidatePermissionsCache();
-}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e750ab23/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
new file mode 100644
index 0000000..9e0dfa9
--- /dev/null
+++ b/src/java/org/apache/cassandra/auth/PermissionsCache.java
@@ -0,0 +1,108 @@
+/*
+ * 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 java.util.Set;
+import java.util.concurrent.*;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.concurrent.DebuggableThreadPoolExecutor;
+import org.apache.cassandra.utils.Pair;
+
+public class PermissionsCache
+{
+ private static final Logger logger = LoggerFactory.getLogger(PermissionsCache.class);
+
+ private final ThreadPoolExecutor cacheRefreshExecutor = new DebuggableThreadPoolExecutor("PermissionsCacheRefresh",
+ Thread.NORM_PRIORITY);
+ private final IAuthorizer authorizer;
+ private final LoadingCache<Pair<AuthenticatedUser, IResource>, Set<Permission>> cache;
+
+ public PermissionsCache(int validityPeriod, int updateInterval, int maxEntries, IAuthorizer authorizer)
+ {
+ this.authorizer = authorizer;
+ this.cache = initCache(validityPeriod, updateInterval, maxEntries);
+ }
+
+ public Set<Permission> getPermissions(AuthenticatedUser user, IResource resource)
+ {
+ if (cache == null)
+ return authorizer.authorize(user, resource);
+
+ try
+ {
+ return cache.get(Pair.create(user, resource));
+ }
+ catch (ExecutionException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private LoadingCache<Pair<AuthenticatedUser, IResource>, Set<Permission>> initCache(int validityPeriod,
+ int updateInterval,
+ int maxEntries)
+ {
+ if (authorizer instanceof AllowAllAuthorizer)
+ return null;
+
+ if (validityPeriod <= 0)
+ return null;
+
+ return CacheBuilder.newBuilder()
+ .refreshAfterWrite(updateInterval, TimeUnit.MILLISECONDS)
+ .expireAfterWrite(validityPeriod, TimeUnit.MILLISECONDS)
+ .maximumSize(maxEntries)
+ .build(new CacheLoader<Pair<AuthenticatedUser, IResource>, Set<Permission>>()
+ {
+ public Set<Permission> load(Pair<AuthenticatedUser, IResource> userResource)
+ {
+ return authorizer.authorize(userResource.left, userResource.right);
+ }
+
+ public ListenableFuture<Set<Permission>> reload(final Pair<AuthenticatedUser, IResource> userResource,
+ final Set<Permission> oldValue)
+ {
+ ListenableFutureTask<Set<Permission>> task = ListenableFutureTask.create(new Callable<Set<Permission>>()
+ {
+ public Set<Permission>call() throws Exception
+ {
+ try
+ {
+ return authorizer.authorize(userResource.left, userResource.right);
+ }
+ catch (Exception e)
+ {
+ logger.debug("Error performing async refresh of user permissions", e);
+ throw e;
+ }
+ }
+ });
+ cacheRefreshExecutor.execute(task);
+ return task;
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e750ab23/src/java/org/apache/cassandra/config/Config.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java
index aab5025..4dd71aa 100644
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@ -43,6 +43,8 @@ public class Config
public String authenticator;
public String authorizer;
public int permissions_validity_in_ms = 2000;
+ public int permissions_cache_max_entries = 1000;
+ public int permissions_update_interval_in_ms = -1;
/* Hashing strategy Random or OPHF */
public String partitioner;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e750ab23/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 92ef79a..2bfdb16 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -578,11 +578,17 @@ public class DatabaseDescriptor
return conf.permissions_validity_in_ms;
}
- public static void setPermissionsValidity(int timeout)
+ public static int getPermissionsCacheMaxEntries()
{
- conf.permissions_validity_in_ms = timeout;
+ return conf.permissions_cache_max_entries;
}
+ public static int getPermissionsUpdateInterval()
+ {
+ return conf.permissions_update_interval_in_ms == -1
+ ? conf.permissions_validity_in_ms
+ : conf.permissions_update_interval_in_ms;
+ }
public static int getThriftFramedTransportSize()
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e750ab23/src/java/org/apache/cassandra/service/ClientState.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/ClientState.java b/src/java/org/apache/cassandra/service/ClientState.java
index 7611a14..63c9431 100644
--- a/src/java/org/apache/cassandra/service/ClientState.java
+++ b/src/java/org/apache/cassandra/service/ClientState.java
@@ -19,7 +19,6 @@ package org.apache.cassandra.service;
import java.net.SocketAddress;
import java.util.*;
-import java.util.concurrent.ExecutionException;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
@@ -40,7 +39,6 @@ import org.apache.cassandra.exceptions.UnauthorizedException;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.thrift.ThriftValidation;
import org.apache.cassandra.utils.FBUtilities;
-import org.apache.cassandra.utils.Pair;
import org.apache.cassandra.utils.SemanticVersion;
/**
@@ -313,17 +311,6 @@ public class ClientState
private Set<Permission> authorize(IResource resource)
{
- // AllowAllAuthorizer or manually disabled caching.
- if (Auth.permissionsCache == null)
- return DatabaseDescriptor.getAuthorizer().authorize(user, resource);
-
- try
- {
- return Auth.permissionsCache.get(Pair.create(user, resource));
- }
- catch (ExecutionException e)
- {
- throw new RuntimeException(e);
- }
+ return Auth.getPermissions(user, resource);
}
}