This is an automated email from the ASF dual-hosted git repository.

joemcdonnell pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git

commit 2280c1362ebdc9f9827e1928e7b3a822b1533302
Author: Andrew Sherman <[email protected]>
AuthorDate: Mon Nov 18 16:27:17 2024 -0800

    IMPALA-12943: Document Admission Control User Quotas.
    
    Document the feature introduced in IMPALA-12345. Add a few more tests to
    the QuotaExamples test which demonstrate the examples used in the
    docs.
    
    Clarify in docs and code the behavior when a user is a member of more
    than one group for which there are rules. In this case the least
    restrictive rule applies.
    
    Also document the '--max_hs2_sessions_per_user' flag introduced in
    IMPALA-12264.
    
    Change-Id: I82e044adb072a463a1e4f74da71c8d7d48292970
    Reviewed-on: http://gerrit.cloudera.org:8080/22100
    Reviewed-by: Impala Public Jenkins <[email protected]>
    Tested-by: Impala Public Jenkins <[email protected]>
---
 be/src/scheduling/admission-controller-test.cc     | 106 +++++++-
 be/src/scheduling/admission-controller.cc          |  64 ++++-
 be/src/scheduling/admission-controller.h           |   7 +-
 docs/impala_keydefs.ditamap                        |   1 +
 docs/topics/impala_admission_config.xml            | 289 ++++++++++++++++++++-
 docs/topics/impala_client.xml                      |  16 ++
 .../apache/impala/util/TestRequestPoolService.java |   4 +
 fe/src/test/resources/fair-scheduler-test3.xml     |  24 ++
 tests/custom_cluster/test_admission_controller.py  |   6 +-
 9 files changed, 486 insertions(+), 31 deletions(-)

diff --git a/be/src/scheduling/admission-controller-test.cc 
b/be/src/scheduling/admission-controller-test.cc
index df5e37bb7..8dcdbc569 100644
--- a/be/src/scheduling/admission-controller-test.cc
+++ b/be/src/scheduling/admission-controller-test.cc
@@ -105,7 +105,7 @@ class AdmissionControllerTest : public testing::Test {
     FLAGS_injected_group_members_debug_only = "group0:userA;"
                                               "group1:user1,user3;"
                                               "dev:alice,deborah;"
-                                              "it:bob,fiona;"
+                                              "it:bob,fiona,geeta;"
                                               "support:claire,geeta,howard;";
     ASSERT_OK(test_env_->Init());
   }
@@ -973,7 +973,7 @@ TEST_F(AdmissionControllerTest, UserAndGroupQuotas) {
   ASSERT_FALSE(admission_controller->CanAdmitQuota(
       *schedule_state, config_e, config_root, &not_admitted_reason));
   EXPECT_STR_CONTAINS(not_admitted_reason,
-      "current per-user load 3 for user userA is at or above the user limit 
3");
+      "current per-user load 3 for user 'userA' is at or above the user limit 
3");
 
   // If UserA's load is 2 it should be admitted because the user rule takes 
precedence
   // over the wildcard rule.
@@ -990,7 +990,7 @@ TEST_F(AdmissionControllerTest, UserAndGroupQuotas) {
   ASSERT_FALSE(admission_controller->CanAdmitQuota(
       *schedule_state, config_e, config_root, &not_admitted_reason));
   EXPECT_STR_CONTAINS(not_admitted_reason,
-      "current per-user load 3 for user user2 is at or above the wildcard 
limit 1");
+      "current per-user load 3 for user 'user2' is at or above the wildcard 
limit 1");
 
   pool_stats->agg_user_loads_.clear_key(USER2);
   ASSERT_TRUE(admission_controller->CanAdmitQuota(
@@ -1004,8 +1004,8 @@ TEST_F(AdmissionControllerTest, UserAndGroupQuotas) {
   ASSERT_FALSE(admission_controller->CanAdmitQuota(
       *schedule_state, config_e, config_root, &not_admitted_reason));
   EXPECT_STR_CONTAINS(not_admitted_reason,
-      "current per-group load 2 for user user3 in group group1 is at or above 
the group "
-      "limit 2");
+      "current per-group load 2 for user 'user3' in group 'group1' is at or 
above the "
+      "group limit 2");
 
   // Quota set to 0 disallows entry.
   schedule_state = MakeScheduleState(QUEUE_E, config_e, host_count, 30L * 
MEGABYTE,
@@ -1014,7 +1014,7 @@ TEST_F(AdmissionControllerTest, UserAndGroupQuotas) {
   ASSERT_FALSE(admission_controller->CanAdmitQuota(
       *schedule_state, config_e, config_root, &not_admitted_reason));
   EXPECT_STR_CONTAINS(not_admitted_reason,
-      "current per-user load 0 for user userH is at or above the user limit 
0");
+      "current per-user load 0 for user 'userH' is at or above the user limit 
0");
 
   ScheduleState* bad_user_state = MakeScheduleState(QUEUE_E, config_e, 
host_count,
       30L * MEGABYTE, ImpalaServer::DEFAULT_EXECUTOR_GROUP_NAME, 
"[email protected]@d.com");
@@ -1025,7 +1025,7 @@ TEST_F(AdmissionControllerTest, UserAndGroupQuotas) {
 
 /// Test CanAdmitRequest in the context of user and group quotas.
 // Group membership is injected in AdmissionControllerTest::Setup().
-// The user 'bob' is in group 'it'.
+// The users 'bob' and 'fiona' are in group 'it'.
 // The user 'howard' is in group 'support'.
 TEST_F(AdmissionControllerTest, QuotaExamples) {
   // Pass the paths of the configuration files as command line flags.
@@ -1033,20 +1033,79 @@ TEST_F(AdmissionControllerTest, QuotaExamples) {
   FLAGS_llama_site_path = GetResourceFile("llama-site-test2.xml");
   string not_admitted_reason;
 
+  // Alice can run 3 queries, because the more specific rule for 'alice' 
overrides
+  // the less-specific wildcard rule.
+  ASSERT_TRUE(can_queue("alice", 3, 2, true, &not_admitted_reason));
+  ASSERT_FALSE(can_queue("alice", 4, 12, true, &not_admitted_reason));
+  ASSERT_EQ(
+      "current per-user load 4 for user 'alice' is at or above the user limit 
4 in pool "
+      "'" + QUEUE_SMALL + "'",
+      not_admitted_reason);
+
+  // Bob can run 2 queries, because the more specific group rule for 'it' 
overrides
+  // the less-specific wildcard rule.
   ASSERT_TRUE(can_queue("bob", 1, 1, true, &not_admitted_reason));
+  ASSERT_FALSE(can_queue("bob", 2, 1, true, &not_admitted_reason));
+  ASSERT_EQ(
+      "current per-group load 2 for user 'bob' in group 'it' is at or above 
the group "
+      "limit 2 in pool '"
+          + QUEUE_SMALL + "'",
+      not_admitted_reason);
 
-  // Howard has a limit of 4 at root level.
+  ASSERT_FALSE(can_queue("claire", 5, 0, true, &not_admitted_reason));
+  ASSERT_EQ(
+      "current per-group load 5 for user 'claire' in group 'support' is at or 
above the "
+      "group limit 5 in pool '"
+          + QUEUE_SMALL + "'",
+      not_admitted_reason);
+
+  // Fiona can run 3 queries, because the more specific user rule overrides
+  // the less-specific group rule.
+  ASSERT_TRUE(can_queue("fiona", 2, 1, true, &not_admitted_reason));
+  ASSERT_FALSE(can_queue("fiona", 3, 1, true, &not_admitted_reason));
+  ASSERT_EQ(
+      "current per-user load 3 for user 'fiona' is at or above the user limit 
3 in pool '"
+          + QUEUE_SMALL + "'",
+      not_admitted_reason);
+
+  // Geeta is in 2 groups: 'it' and 'support'.
+  // Group 'it' restricts her to running 2 queries in the small pool.
+  // Group 'support' restricts her to running 5 queries in the small pool.
+  // The 'support' rule is the least restrictive, so she can run 5 queries in 
the small
+  // pool.
+  ASSERT_TRUE(can_queue("geeta", 4, 1, true, &not_admitted_reason));
+  ASSERT_FALSE(can_queue("geeta", 5, 1, true, &not_admitted_reason));
+  ASSERT_EQ("current per-group load 5 for user 'geeta' in group 'support' is 
at or above "
+            "the group limit 5 in pool '"
+          + QUEUE_SMALL + "' (Ignored Group Quotas 'it':2)",
+      not_admitted_reason);
+
+  // Howard has a limit of 4 at root level, and limit of 100 in the small pool.
+  // Arguably this small pool configuration does not make sense as it will 
never have any
+  // effect.
+  ASSERT_TRUE(can_queue("howard", 2, 1, true, &not_admitted_reason));
   ASSERT_FALSE(can_queue("howard", 3, 1, true, &not_admitted_reason));
   ASSERT_EQ(
-      "current per-user load 4 for user howard is at or above the user limit 4 
in pool "
-          + QUEUE_ROOT,
+      "current per-user load 4 for user 'howard' is at or above the user limit 
4 in pool"
+      " '" + QUEUE_ROOT
+          + "'",
       not_admitted_reason);
 
   // Iris is not in any groups and so hits the large pool wildcard limit.
   ASSERT_FALSE(can_queue("iris", 0, 1, false, &not_admitted_reason));
   ASSERT_EQ(
-      "current per-user load 1 for user iris is at or above the wildcard limit 
1 in pool "
-          + QUEUE_LARGE,
+      "current per-user load 1 for user 'iris' is at or above the wildcard 
limit 1 in "
+      "pool '"
+          + QUEUE_LARGE + "'",
+      not_admitted_reason);
+
+  // Jade has a limit of 100 at root level, and limit of 8 in the small pool.
+  // This is an example where there are User Quotas at both root and pool 
level.
+  ASSERT_TRUE(can_queue("jade", 7, 0, true, &not_admitted_reason));
+  ASSERT_FALSE(can_queue("jade", 8, 0, true, &not_admitted_reason));
+  ASSERT_EQ(
+      "current per-user load 8 for user 'jade' is at or above the user limit 8 
in pool '"
+          + QUEUE_SMALL + "'",
       not_admitted_reason);
 }
 
@@ -2174,4 +2233,27 @@ TEST_F(AdmissionControllerTest, AggregatedUserLoads) {
   ASSERT_EQ(4, user_loads.decrement(USER3));
 }
 
+/// Unit test for AdmissionController::HasSufficientGroupQuota
+TEST_F(AdmissionControllerTest, HasSufficientGroupQuota) {
+  const vector<std::string> groups = {"group1", "group2", "group3", "group4"};
+  TPoolConfig config;
+  std::map<std::string, int32_t> limits = {
+      // These limits are unordered so that we touch both code paths that add 
a group to
+      // extra_groups in HasSufficientGroupQuota().
+      {"group1", 3},
+      {"group2", 1},
+      {"group4", 4}};
+  config.__set_group_query_limits(limits);
+
+  string quota_exceeded_reason;
+  bool key_matched;
+  bool ok;
+  ok = AdmissionController::HasSufficientGroupQuota(
+      USER1, groups, config, "poolname", 100, &quota_exceeded_reason, 
&key_matched);
+  EXPECT_FALSE(ok);
+  EXPECT_STR_CONTAINS(quota_exceeded_reason,
+      "current per-group load 100 for user 'user1' in group 'group4' is at or 
above the "
+      "group limit 4 in pool 'poolname' (Ignored Group Quotas 'group2':1 
'group1':3)");
+}
+
 } // end namespace impala
diff --git a/be/src/scheduling/admission-controller.cc 
b/be/src/scheduling/admission-controller.cc
index 43d7b10c9..4a75ef382 100644
--- a/be/src/scheduling/admission-controller.cc
+++ b/be/src/scheduling/admission-controller.cc
@@ -289,15 +289,17 @@ const string HOST_SLOT_NOT_AVAILABLE = "Not enough 
admission control slots avail
                                        "in use.";
 
 // $0 = current load for user, $1 = user name, $2 = per-user quota, $3 is pool 
name
-const string USER_QUOTA_EXCEEDED =
-    "current per-user load $0 for user $1 is at or above the user limit $2 in 
pool $3";
-const string USER_WILDCARD_QUOTA_EXCEEDED = "current per-user load $0 for user 
$1 is at "
-                                            "or above the wildcard limit $2 in 
pool $3";
+const string USER_QUOTA_EXCEEDED = "current per-user load $0 for user '$1' is 
at or "
+                                   "above the user limit $2 in pool '$3'";
+const string USER_WILDCARD_QUOTA_EXCEEDED = "current per-user load $0 for user 
'$1' is at"
+                                            " or above the wildcard limit $2 
in pool "
+                                            "'$3'";
 
 // $0 = current load for user, $1 = user name, $2 = group name, $3 = per-user 
quota,
 // $4 is pool name.
-const string GROUP_QUOTA_EXCEEDED = "current per-group load $0 for user $1 in 
group $2 "
-                                    "is at or above the group limit $3 in pool 
$4";
+const string GROUP_QUOTA_EXCEEDED = "current per-group load $0 for user '$1' 
in group "
+                                    "'$2' is at or above the group limit $3 in 
pool '$4'"
+                                    "$5";
 
 // $0 = user name
 const string BAD_USER_NAME = "cannot parse user name $0";
@@ -1239,23 +1241,59 @@ bool AdmissionController::HasSufficientGroupQuota(const 
string& user,
                << status.GetDetail();
     return false;
   }
+  const vector<std::string>& groups = res.groups;
 
-  // For every group see if there is a limit and enforce it.
+  return HasSufficientGroupQuota(
+      user, groups, pool_cfg, pool_name, user_load, quota_exceeded_reason, 
key_matched);
+}
 
-  for (const string& group : res.groups) {
+bool AdmissionController::HasSufficientGroupQuota(const string& user,
+    const vector<std::string>& groups,
+    const TPoolConfig& pool_cfg, const string& pool_name, int64_t user_load,
+    string* quota_exceeded_reason, bool* key_matched) {
+  // Find the highest group limit and enforce it.
+  int64_t highest_group_limit = -1;
+  string highest_group;
+  string extra_group_desc;
+  std::vector<std::pair<std::string, int>> extra_groups;
+
+  for (const string& group : groups) {
     auto it = pool_cfg.group_query_limits.find(group);
     int64_t group_limit = 0;
     if (it != pool_cfg.group_query_limits.end()) {
       // There is a per-user limit for the delegated user.
       group_limit = it->second;
-      if (user_load + 1 > group_limit) {
-        *quota_exceeded_reason = Substitute(
-            GROUP_QUOTA_EXCEEDED, user_load, user, group, group_limit, 
pool_name);
-        return false;
+      if (group_limit > highest_group_limit) {
+        if (highest_group_limit != -1) {
+          // Replacing old group, save it for error string.
+          extra_groups.emplace_back(highest_group, highest_group_limit);
+        }
+        highest_group_limit = group_limit;
+        highest_group = group;
+      } else {
+        // Ignoring this, group, save it for error string.
+        extra_groups.emplace_back(group, group_limit);
       }
-      *key_matched = true;
     }
   }
+  if (!extra_groups.empty()) {
+    // Describe the Group Quotas we did not use.
+    std::stringstream ss;
+    ss << " (Ignored Group Quotas";
+    for (const auto& pair : extra_groups) {
+      ss << " '" << pair.first << "':" << pair.second;
+    }
+    ss << ")";
+    extra_group_desc = ss.str();
+  }
+  if (highest_group_limit != -1) {
+    if (user_load + 1 > highest_group_limit) {
+      *quota_exceeded_reason = Substitute(GROUP_QUOTA_EXCEEDED, user_load, 
user,
+          highest_group, highest_group_limit, pool_name, extra_group_desc);
+      return false;
+    }
+    *key_matched = true;
+  }
   return true;
 }
 
diff --git a/be/src/scheduling/admission-controller.h 
b/be/src/scheduling/admission-controller.h
index e63d6c2d4..ac6f3178e 100644
--- a/be/src/scheduling/admission-controller.h
+++ b/be/src/scheduling/admission-controller.h
@@ -1301,6 +1301,10 @@ class AdmissionController {
   bool HasSufficientGroupQuota(const string& user, const TPoolConfig& pool_cfg,
       const string& pool_name, int64_t user_load, string* 
quota_exceeded_reason,
       bool* key_matched) const;
+  static bool HasSufficientGroupQuota(const string& user,
+      const vector<std::string>& groups, const TPoolConfig& pool_cfg,
+      const string& pool_name, int64_t user_load, string* 
quota_exceeded_reason,
+      bool* key_matched);
 
   /// Returns available memory and slots of the executor group.
   const std::pair<int64_t, int64_t> GetAvailableMemAndSlots(
@@ -1383,13 +1387,14 @@ class AdmissionController {
   FRIEND_TEST(AdmissionControllerTest, DedicatedCoordAdmissionChecks);
   FRIEND_TEST(AdmissionControllerTest, DedicatedCoordScheduleState);
   FRIEND_TEST(AdmissionControllerTest, DequeueLoop);
+  FRIEND_TEST(AdmissionControllerTest, EraseHostStats);
   FRIEND_TEST(AdmissionControllerTest, GetMaxToDequeue);
+  FRIEND_TEST(AdmissionControllerTest, HasSufficientGroupQuota);
   FRIEND_TEST(AdmissionControllerTest, PoolStats);
   FRIEND_TEST(AdmissionControllerTest, QueryRejection);
   FRIEND_TEST(AdmissionControllerTest, QuotaExamples);
   FRIEND_TEST(AdmissionControllerTest, Simple);
   FRIEND_TEST(AdmissionControllerTest, TopNQueryCheck);
-  FRIEND_TEST(AdmissionControllerTest, EraseHostStats);
   FRIEND_TEST(AdmissionControllerTest, UserAndGroupQuotas);
   friend class AdmissionControllerTest;
 };
diff --git a/docs/impala_keydefs.ditamap b/docs/impala_keydefs.ditamap
index 644b847b5..12d90be73 100644
--- a/docs/impala_keydefs.ditamap
+++ b/docs/impala_keydefs.ditamap
@@ -10533,6 +10533,7 @@ under the License.
   <keydef href="https://issues.apache.org/jira/browse/IMPALA-9999"; 
scope="external" format="html" keys="IMPALA-9999"/>
 
 <!-- Short form of mapping from Impala release to vendor-specific releases, 
for use in headings. -->
+  <keydef keys="impala45"><topicmeta><keywords><keyword>Impala 
4.5</keyword></keywords></topicmeta></keydef>
   <keydef keys="impala44"><topicmeta><keywords><keyword>Impala 
4.4</keyword></keywords></topicmeta></keydef>
   <keydef keys="impala43"><topicmeta><keywords><keyword>Impala 
4.3</keyword></keywords></topicmeta></keydef>
   <keydef keys="impala42"><topicmeta><keywords><keyword>Impala 
4.2</keyword></keywords></topicmeta></keydef>
diff --git a/docs/topics/impala_admission_config.xml 
b/docs/topics/impala_admission_config.xml
index d7b000ef3..a1b179c13 100644
--- a/docs/topics/impala_admission_config.xml
+++ b/docs/topics/impala_admission_config.xml
@@ -89,7 +89,7 @@ under the License.
         concurrent and queued queries, for different categories of users 
within your
         organization. For details about all the Fair Scheduler configuration 
settings, see the
         <xref
-          
href="http://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html#Configuration";
+          
href="https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html#Configuration";
           scope="external" format="html">Apache
         wiki</xref>.
       </p>
@@ -105,6 +105,15 @@ 
llama.am.throttling.maximum.queued.reservations.<varname>queue_name</varname>
 
impala.admission-control.pool-queue-timeout-ms.<varname>queue_name</varname></ph>
 </codeblock>
 
+      <p>
+        The 
<codeph>llama.am.throttling.maximum.placed.reservations.<varname>queue_name</varname></codeph>
+        setting specifies the number of queries that are allowed to run 
concurrently in this pool.
+      </p>
+      <p>
+        The 
<codeph>llama.am.throttling.maximum.queued.reservations.<varname>queue_name</varname></codeph>
+        setting specifies the number of queries that are allowed to be queued 
in this pool.
+      </p>
+
       <p rev="2.5.0 IMPALA-2538">
         The <codeph>impala.admission-control.pool-queue-timeout-ms</codeph> 
setting specifies
         the timeout value for this pool in milliseconds.
@@ -239,8 +248,284 @@ 
impala.admission-control.pool-queue-timeout-ms.<varname>queue_name</varname></ph
 
     </concept>
 
+  <concept rev="4.5.0" id="user_quotas">
+
+      <title>Configuring User Quotas in the Admission Control 
Configuration</title>
+
+      <conbody>
+
+        <p>
+          Since <keyword keyref="impala45"/>, User Quotas can be used to 
define rules
+          that limit the total count of queries that a user can run 
concurrently.
+          The rules are based on either usernames or group membership.
+          The username is the short username, and groups are
+          <xref
+              
href="https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/GroupsMapping.html";
+              scope="external" format="html">Hadoop Groups
+          </xref>.
+        </p>
+        <p>
+          Queries are counted from when they are queued to when they are 
released.
+          In a cluster without an Admission Daemon, the count of queries is 
synchronized
+          across all coordinators through the Statestore.
+          In this configuration over-admission is possible.
+        </p>
+        <p>
+          If a query fails to pass the User Quota rules then the query will be
+          rejected when it is submitted.
+        </p>
+
+        <p>
+          There are three type of rules:
+          <ul>
+            <li>
+              User Rules (based on a username)
+            </li>
+            <li>
+              Wildcard Rules (which match any username)
+            </li>
+            <li>
+              Group Rules (based on a user’s membership of a group)
+            </li>
+          </ul>
+          The rules can be placed at the Pool level or the Root level:
+          <ul>
+            <li>
+              A rule at the Pool level is evaluated against the number of 
queries a user has running in the pool
+            </li>
+            <li>
+              A rule at the Root level is evaluated against the total number 
of queries a user has running across all pools.
+            </li>
+          </ul>
+        </p>
+
+        <p>
+          The XML tags used for User Quotas are:
+          <ul>
+            <li>
+              <b>userQueryLimit</b> - used to define a User Rule
+            </li>
+            <li>
+              <b>groupQueryLimit</b> - used to define a Group Rule
+            </li>
+            <li>
+              <b>totalCount</b> - used to define the number of queries that 
can run
+              concurrently.
+            </li>
+            <li>
+              <b>user</b> - used to specify a username to define a User Rule,
+              or, by using the wildcard '*', to define a Wildcard Rule.
+            </li>
+            <li>
+              <b>group</b> - in a Group rule, used to specify a group name 
that the
+              rule applies to.
+            </li>
+          </ul>
+          Examples are provided below.
+        </p>
+
+        <p>
+          More specific rules override more general rules.
+          A user rule overrides a group rule or wildcard rule.
+          A group rule overrides a wildcard rule.
+          Pool level rules are evaluated first.
+          If Pool level rules pass, then Root level rules are evaluated.
+        </p>
+
+        <p>
+          Note that the examples of User Quota rules below are incomplete xml 
snippets
+          that would form part of an Admission Control configuration.
+          In particular, they omit the essential <b>aclSubmitApps</b> tag.
+        </p>
+
+        <section>
+          <title>A User Rule</title>
+          <codeblock>
+&lt;userQueryLimit&gt;
+  &lt;user&gt;alice&lt;/user&gt;
+  &lt;user&gt;bob&lt;/user&gt;
+  &lt;totalCount&gt;3&lt;/totalCount&gt;
+&lt;/userQueryLimit&gt;
+          </codeblock>
+          <p>
+            This rule limits the users ‘alice’ and ‘bob’ to each running 3 
queries.
+            This rule could be placed at the Pool level or the Root level.
+          </p>
+        </section>
+
+        <section>
+          <title>A Pool Level Wildcard Rule</title>
+          <codeblock>
+&lt;queue name=&quot;root&quot;&gt;
+  &lt;queue name=&quot;queueE&quot;&gt;
+    &lt;userQueryLimit&gt;
+      &lt;user&gt;*&lt;/user&gt;
+      &lt;totalCount&gt;2&lt;/totalCount&gt;
+    &lt;/userQueryLimit&gt;
+  &lt;/queue&gt;
+...
+          </codeblock>
+          <p>
+            This is a wildcard rule on pool root.queueE that limits
+            all users to running 2 queries in that pool.
+          </p>
+        </section>
+
+        <section>
+          <title>A Root Level User Rule</title>
+          <codeblock>
+&lt;queue name=&quot;root&quot;&gt;
+  &lt;userQueryLimit&gt;
+    &lt;user&gt;userD&lt;/user&gt;
+    &lt;totalCount&gt;2&lt;/totalCount&gt;
+  &lt;/userQueryLimit&gt;
+...
+          </codeblock>
+          <p>
+            This is a Root level user rule that limits userD to 2 queries 
across all
+            pools.
+          </p>
+        </section>
+
+        <section>
+          <title>A Group Rule</title>
+          <codeblock>
+&lt;queue name=&quot;root&quot;&gt;
+  &lt;queue name=&quot;queueE&quot;&gt;
+    &lt;groupQueryLimit&gt;
+      &lt;group&gt;group1&lt;/group&gt;
+      &lt;group&gt;group2&lt;/group&gt;
+      &lt;totalCount&gt;2&lt;/totalCount&gt;
+    &lt;/groupQueryLimit&gt;
+...
+          </codeblock>
+          <p>
+            This rule limits any user in groups group1 or group2 to running 2 
queries
+            in pool root.queueE. Note this rule could also be placed at the 
root level.
+          </p>
+        </section>
+
+        <section>
+          <title>
+            More Specific Rules Override The Less Specific
+          </title>
+          <codeblock>
+&lt;queue name=&quot;group-set-small&quot;&gt;
+  &lt;!-- A user not matched by any other rule can run 1 query in the small 
pool --&gt;
+  &lt;userQueryLimit&gt;
+      &lt;user&gt;*&lt;/user&gt;
+      &lt;totalCount&gt;1&lt;/totalCount&gt;
+  &lt;/userQueryLimit&gt;
+  &lt;!-- The user &apos;alice&apos; can run 4 queries in the small pool --&gt;
+  &lt;userQueryLimit&gt;
+    &lt;user&gt;alice&lt;/user&gt;
+    &lt;totalCount&gt;4&lt;/totalCount&gt;
+  &lt;/userQueryLimit&gt;
+...
+          </codeblock>
+          <p>
+            With this rule, the user ‘alice’ can run 4 queries in 
"root.group-set-small".
+            The more specific User rule overrides the wildcard rule.
+            A user that is not ‘alice’ would be able to run 1 query, per the 
wildcard
+            rule.
+          </p>
+        </section>
+
+        <section>
+          <title>
+            Another Example of More Specific Rules Overriding Less Specific 
Rules
+          </title>
+          <codeblock>
+&lt;queue name=&quot;group-set-small&quot;&gt;
+  &lt;!-- A user not matched by any other rule can run 1 query in the small 
pool --&gt;
+  &lt;userQueryLimit&gt;
+      &lt;user&gt;*&lt;/user&gt;
+      &lt;totalCount&gt;1&lt;/totalCount&gt;
+  &lt;/userQueryLimit&gt;
+  &lt;!-- Members of the group &apos;it&apos; can run 2 queries in the small 
pool --&gt;
+  &lt;groupQueryLimit&gt;
+    &lt;group&gt;it&lt;/group&gt;
+    &lt;totalCount&gt;2&lt;/totalCount&gt;
+  &lt;/groupQueryLimit&gt;
+...
+          </codeblock>
+          <p>
+            Assuming the user ‘bob’ is in the ‘it’ group, they can run 2 
queries
+            in "root.group-set-small".
+            The more specific Group rule overrides the wildcard rule.
+          </p>
+        </section>
+
+        <section>
+          <title>
+            User rules are more specific than group rules.
+          </title>
+          <codeblock>
+&lt;queue name=&quot;group-set-small&quot;&gt;
+  &lt;!-- Members of the group &apos;it&apos; can run 2 queries in the small 
pool --&gt;
+  &lt;groupQueryLimit&gt;
+    &lt;group&gt;it&lt;/group&gt;
+    &lt;totalCount&gt;2&lt;/totalCount&gt;
+  &lt;/groupQueryLimit&gt;
+  &lt;!-- The user &apos;fiona&apos; can run 3 queries in the small pool --&gt;
+  &lt;userQueryLimit&gt;
+    &lt;user&gt;fiona&lt;/user&gt;
+    &lt;totalCount&gt;3&lt;/totalCount&gt;
+  &lt;/userQueryLimit&gt;
+...
+          </codeblock>
+          <p>
+            The user 'fiona' can run 4 queries in "root.group-set-small", 
despite also
+            matching the 'it' group.
+            The more specific user rule overrides the group rule.
+          </p>
+        </section>
+
+        <section>
+          <title>
+            Both pool and root rules must be passed.
+          </title>
+          <codeblock>
+&lt;queue name=&quot;root&quot;&gt;
+  &lt;groupQueryLimit&gt;
+    &lt;group&gt;support&lt;/group&gt;
+    &lt;totalCount&gt;6&lt;/totalCount&gt;
+  &lt;/groupQueryLimit&gt;
+  &lt;queue name=&quot;group-set-small&quot;&gt;
+    &lt;groupQueryLimit&gt;
+      &lt;group&gt;dev&lt;/group&gt;
+      &lt;group&gt;support&lt;/group&gt;
+      &lt;totalCount&gt;5&lt;/totalCount&gt;
+    &lt;/groupQueryLimit&gt;
+  &lt;/queue&gt;
+...
+          </codeblock>
+          <p>
+            Members of group 'support' are limited to run 6 queries across all 
the pools.
+            Members of groups 'dev' and 'support' can run 5 queries
+            in "root.group-set-small".
+            To run, both rules must be passed.
+            The Pool level rule is evaluated first, so that an error 
mentioning that pool
+            will be seen in the case where both rules would fail.
+          </p>
+          <p>
+            A user may be a member of more than one group, which means that 
more than one
+            Group Rule may apply to a user at the Pool or Root level.
+            In this case, at each level, the least restrictive rule is applied.
+            For example, if the user is in 2 groups: 'it' and 'support', where
+            group 'it' restricts her to running 2 queries, and group 'support' 
restricts
+            her to running 5 queries, then the 'support' rule is the least 
restrictive,
+            so she can run 5 queries in the pool.
+          </p>
+        </section>
+
+      </conbody>
+
+    </concept>
+
   </concept>
 
+
   <concept id="concept_zy4_vxz_jgb">
 
     <title>Configuring Cluster-wide Admission Control</title>
@@ -433,7 +718,7 @@ 
impala.admission-control.pool-queue-timeout-ms.<varname>queue_name</varname></ph
               that can go in this file, as described below. For details about 
all the Fair
               Scheduler configuration settings, see the
               <xref
-                
href="http://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html#Configuration";
+                
href="https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html#Configuration";
                 scope="external" format="html">Apache
               wiki</xref>.
             </p>
diff --git a/docs/topics/impala_client.xml b/docs/topics/impala_client.xml
index 45f79c4f3..4c8ee6e2d 100644
--- a/docs/topics/impala_client.xml
+++ b/docs/topics/impala_client.xml
@@ -318,6 +318,22 @@ under the License.
           </dd>
 
         </dlentry>
+
+        <dlentry>
+
+          <dt>
+            --max_hs2_sessions_per_user
+          </dt>
+
+          <dd>
+            Specifies the maximum allowed number of HiveServer2 sessions that 
can be
+            opened by any single connected user on a coordinator.
+            <p>
+              The default is -1. Setting to -1 or 0 disables this feature.
+            </p>
+          </dd>
+
+        </dlentry>
       </dl>
 
     </section>
diff --git 
a/fe/src/test/java/org/apache/impala/util/TestRequestPoolService.java 
b/fe/src/test/java/org/apache/impala/util/TestRequestPoolService.java
index 9965e4acd..85f356236 100644
--- a/fe/src/test/java/org/apache/impala/util/TestRequestPoolService.java
+++ b/fe/src/test/java/org/apache/impala/util/TestRequestPoolService.java
@@ -325,6 +325,7 @@ public class TestRequestPoolService {
       {
         put("*", 8);
         put("howard", 4);
+        put("jade", 100);
       }
     };
     Assert.assertEquals(rootUserExpected, rootConfig.user_query_limits);
@@ -338,6 +339,9 @@ public class TestRequestPoolService {
       {
         put("*", 1);
         put("alice", 4);
+        put("fiona", 3);
+        put("howard", 100);
+        put("jade", 8);
       }
     };
     Assert.assertEquals(smallUserExpected, smallConfig.user_query_limits);
diff --git a/fe/src/test/resources/fair-scheduler-test3.xml 
b/fe/src/test/resources/fair-scheduler-test3.xml
index 4e859a02d..3bbd661a8 100644
--- a/fe/src/test/resources/fair-scheduler-test3.xml
+++ b/fe/src/test/resources/fair-scheduler-test3.xml
@@ -19,6 +19,12 @@
       <totalCount>4</totalCount>
     </userQueryLimit>
 
+    <!-- User 'jade' is limited to running 100 queries across the cluster -->
+    <userQueryLimit>
+      <user>jade</user>
+      <totalCount>100</totalCount>
+    </userQueryLimit>
+
     <queue name="group-set-small">
       <!-- A user not matched by any other rule can run 1 query in the small 
pool -->
       <userQueryLimit>
@@ -44,6 +50,24 @@
         <user>alice</user>
         <totalCount>4</totalCount>
       </userQueryLimit>
+
+      <!-- The user 'fiona' can run 3 queries in the small pool -->
+      <userQueryLimit>
+        <user>fiona</user>
+        <totalCount>3</totalCount>
+      </userQueryLimit>
+
+      <!-- User 'howard' is limited to running 100 queries in the small pool 
-->
+      <userQueryLimit>
+        <user>howard</user>
+        <totalCount>100</totalCount>
+      </userQueryLimit>
+
+      <!-- User 'jade' is limited to running 8 queries in the small pool -->
+      <userQueryLimit>
+        <user>jade</user>
+        <totalCount>8</totalCount>
+      </userQueryLimit>
     </queue>
 
     <queue name="group-set-large">
diff --git a/tests/custom_cluster/test_admission_controller.py 
b/tests/custom_cluster/test_admission_controller.py
index b105dfe63..1d03c23fd 100644
--- a/tests/custom_cluster/test_admission_controller.py
+++ b/tests/custom_cluster/test_admission_controller.py
@@ -1307,7 +1307,7 @@ class 
TestAdmissionController(TestAdmissionControllerBase, HS2TestSuite):
                              pool_to_fail=None):
     query_handles = []
     type = "group" if group_name else "user"
-    group_description = " in group " + group_name if group_name else ""
+    group_description = " in group '" + group_name + "'" if group_name else ""
     pool_that_fails = pool_to_fail if pool_to_fail else pool
     for i in range(limit):
       impalad = self.cluster.impalads[i % 2]
@@ -1328,8 +1328,8 @@ class 
TestAdmissionController(TestAdmissionControllerBase, HS2TestSuite):
     except Exception as e:
       # Construct the expected error message.
       expected = ("Rejected query from pool {pool}: current per-{type} load 
{limit} for "
-                  "user {user}{group_description} is at or above the 
{err_type} limit "
-                  "{limit} in pool {pool_that_fails}".
+                  "user '{user}'{group_description} is at or above the 
{err_type} limit "
+                  "{limit} in pool '{pool_that_fails}'".
                   format(pool=pool, type=type, limit=limit, user=user,
                          group_description=group_description, 
err_type=err_type,
                          pool_that_fails=pool_that_fails))

Reply via email to