This is an automated email from the ASF dual-hosted git repository. kturner pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/accumulo-access.git
The following commit(s) were added to refs/heads/main by this push: new 715613d Avoids looping when evaluating single set of authorizations (#71) 715613d is described below commit 715613d9e7601ae38f7f7033131a7b7bb70cbb89 Author: Keith Turner <ktur...@apache.org> AuthorDate: Mon Jul 8 14:33:27 2024 -0700 Avoids looping when evaluating single set of authorizations (#71) Simplified the code to avoid looping when there is a single set of authorizations. This change was made in an attempt to improve performance and it did very slightly. Before this change saw the following. ``` Benchmark Mode Cnt Score Error Units AccessExpressionBenchmark.measureEvaluation thrpt 12 13.609 ± 0.300 ops/us ``` And after this change saw the following. ``` Benchmark Mode Cnt Score Error Units AccessExpressionBenchmark.measureEvaluation thrpt 12 13.806 ± 0.254 ops/us ``` The performance improvement is slight so maybe not worthwhile. However this change simplified the evaluator code a good bit, so that is worthwhile. --- .../apache/accumulo/access/AccessEvaluator.java | 5 +- .../accumulo/access/AccessEvaluatorImpl.java | 73 +++++----------------- .../accumulo/access/MultiAccessEvaluatorImpl.java | 56 +++++++++++++++++ 3 files changed, 76 insertions(+), 58 deletions(-) diff --git a/src/main/java/org/apache/accumulo/access/AccessEvaluator.java b/src/main/java/org/apache/accumulo/access/AccessEvaluator.java index b3199bc..d80f2e1 100644 --- a/src/main/java/org/apache/accumulo/access/AccessEvaluator.java +++ b/src/main/java/org/apache/accumulo/access/AccessEvaluator.java @@ -19,6 +19,7 @@ package org.apache.accumulo.access; import java.util.Collection; +import java.util.List; /** * This class is used to decide if an entity with a given set of authorizations can access @@ -84,7 +85,7 @@ public interface AccessEvaluator { * @return AccessEvaluator object */ static AccessEvaluator of(Authorizations authorizations) { - return new AccessEvaluatorImpl(AccessEvaluatorImpl.convert(authorizations)); + return new AccessEvaluatorImpl(authorizations); } /** @@ -149,7 +150,7 @@ public interface AccessEvaluator { * */ static AccessEvaluator of(Collection<Authorizations> authorizationSets) { - return new AccessEvaluatorImpl(AccessEvaluatorImpl.convert(authorizationSets)); + return new MultiAccessEvaluatorImpl(authorizationSets); } /** diff --git a/src/main/java/org/apache/accumulo/access/AccessEvaluatorImpl.java b/src/main/java/org/apache/accumulo/access/AccessEvaluatorImpl.java index 6c5daed..29feb1a 100644 --- a/src/main/java/org/apache/accumulo/access/AccessEvaluatorImpl.java +++ b/src/main/java/org/apache/accumulo/access/AccessEvaluatorImpl.java @@ -24,17 +24,13 @@ import static org.apache.accumulo.access.ByteUtils.QUOTE; import static org.apache.accumulo.access.ByteUtils.isQuoteOrSlash; import static org.apache.accumulo.access.ByteUtils.isQuoteSymbol; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.function.Predicate; //this class is intentionally package private and should never be made public final class AccessEvaluatorImpl implements AccessEvaluator { - private final Collection<Predicate<BytesWrapper>> authorizedPredicates; + private final Predicate<BytesWrapper> authorizedPredicate; private static final byte[] EMPTY = new byte[0]; @@ -43,57 +39,28 @@ final class AccessEvaluatorImpl implements AccessEvaluator { private static final ThreadLocal<Tokenizer> tokenizers = ThreadLocal.withInitial(() -> new Tokenizer(EMPTY)); - static Collection<List<byte[]>> convert(Collection<Authorizations> authorizationSets) { - final List<List<byte[]>> authorizationLists = new ArrayList<>(authorizationSets.size()); - for (final Authorizations authorizations : authorizationSets) { - final Set<String> authSet = authorizations.asSet(); - final List<byte[]> authList = new ArrayList<>(authSet.size()); - for (final String auth : authSet) { - authList.add(auth.getBytes(UTF_8)); - } - authorizationLists.add(authList); - } - return authorizationLists; - } - - static Collection<List<byte[]>> convert(Authorizations authorizations) { - final Set<String> authSet = authorizations.asSet(); - final List<byte[]> authList = new ArrayList<>(authSet.size()); - for (final String auth : authSet) { - authList.add(auth.getBytes(UTF_8)); - } - return Collections.singletonList(authList); - } - /** * Create an AccessEvaluatorImpl using an Authorizer object */ AccessEvaluatorImpl(Authorizer authorizationChecker) { - this.authorizedPredicates = List.of(auth -> authorizationChecker.isAuthorized(unescape(auth))); + this.authorizedPredicate = auth -> authorizationChecker.isAuthorized(unescape(auth)); } /** * Create an AccessEvaluatorImpl using a collection of authorizations */ - AccessEvaluatorImpl(final Collection<List<byte[]>> authorizationSets) { - - for (final List<byte[]> auths : authorizationSets) { - for (final byte[] auth : auths) { - if (auth.length == 0) { - throw new IllegalArgumentException("Empty authorization"); - } + AccessEvaluatorImpl(Authorizations authorizations) { + var authsSet = authorizations.asSet(); + final Set<BytesWrapper> authBytes = new HashSet<>(authsSet.size()); + for (String authorization : authsSet) { + var auth = authorization.getBytes(UTF_8); + if (auth.length == 0) { + throw new IllegalArgumentException("Empty authorization"); } + authBytes.add(new BytesWrapper(AccessEvaluatorImpl.escape(auth, false))); } - final List<Predicate<BytesWrapper>> predicates = new ArrayList<>(authorizationSets.size()); - for (final List<byte[]> authorizationList : authorizationSets) { - final Set<BytesWrapper> authBytes = new HashSet<>(authorizationList.size()); - for (final byte[] authorization : authorizationList) { - authBytes.add(new BytesWrapper(AccessEvaluatorImpl.escape(authorization, false))); - } - predicates.add((auth) -> authBytes.contains(auth)); - } - authorizedPredicates = List.copyOf(predicates); + authorizedPredicate = authBytes::contains; } static String unescape(BytesWrapper auth) { @@ -190,17 +157,11 @@ final class AccessEvaluatorImpl implements AccessEvaluator { var bytesWrapper = lookupWrappers.get(); var tokenizer = tokenizers.get(); - for (var auths : authorizedPredicates) { - tokenizer.reset(accessExpression); - Predicate<Tokenizer.AuthorizationToken> atp = authToken -> { - bytesWrapper.set(authToken.data, authToken.start, authToken.len); - return auths.test(bytesWrapper); - }; - if (!ParserEvaluator.parseAccessExpression(tokenizer, atp, authToken -> true)) { - return false; - } - } - return true; + tokenizer.reset(accessExpression); + Predicate<Tokenizer.AuthorizationToken> atp = authToken -> { + bytesWrapper.set(authToken.data, authToken.start, authToken.len); + return authorizedPredicate.test(bytesWrapper); + }; + return ParserEvaluator.parseAccessExpression(tokenizer, atp, authToken -> true); } - } diff --git a/src/main/java/org/apache/accumulo/access/MultiAccessEvaluatorImpl.java b/src/main/java/org/apache/accumulo/access/MultiAccessEvaluatorImpl.java new file mode 100644 index 0000000..067310f --- /dev/null +++ b/src/main/java/org/apache/accumulo/access/MultiAccessEvaluatorImpl.java @@ -0,0 +1,56 @@ +/* + * 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 + * + * https://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.accumulo.access; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +class MultiAccessEvaluatorImpl implements AccessEvaluator { + final List<AccessEvaluatorImpl> evaluators; + + MultiAccessEvaluatorImpl(Collection<Authorizations> authorizationSets) { + evaluators = new ArrayList<>(authorizationSets.size()); + for (Authorizations authorizations : authorizationSets) { + evaluators.add(new AccessEvaluatorImpl(authorizations)); + } + } + + @Override + public boolean canAccess(String accessExpression) throws InvalidAccessExpressionException { + return canAccess(accessExpression.getBytes(UTF_8)); + } + + @Override + public boolean canAccess(byte[] accessExpression) throws InvalidAccessExpressionException { + for (AccessEvaluatorImpl evaluator : evaluators) { + if (!evaluator.canAccess(accessExpression)) { + return false; + } + } + return true; + } + + @Override + public boolean canAccess(AccessExpression accessExpression) { + return canAccess(accessExpression.getExpression()); + } +}