cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From alek...@apache.org
Subject cassandra git commit: Introduce background cache refreshing to permissions cache
Date Fri, 09 Jan 2015 01:03:48 GMT
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 <sam@beobal.com>
Authored: Fri Jan 9 04:02:32 2015 +0300
Committer: Aleksey Yeschenko <aleksey@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);
     }
 }


Mime
View raw message