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, ¬_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, ¬_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, ¬_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, ¬_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, ¬_admitted_reason)); + ASSERT_FALSE(can_queue("alice", 4, 12, true, ¬_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, ¬_admitted_reason)); + ASSERT_FALSE(can_queue("bob", 2, 1, true, ¬_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, ¬_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, ¬_admitted_reason)); + ASSERT_FALSE(can_queue("fiona", 3, 1, true, ¬_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, ¬_admitted_reason)); + ASSERT_FALSE(can_queue("geeta", 5, 1, true, ¬_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, ¬_admitted_reason)); ASSERT_FALSE(can_queue("howard", 3, 1, true, ¬_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, ¬_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, ¬_admitted_reason)); + ASSERT_FALSE(can_queue("jade", 8, 0, true, ¬_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, "a_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> +<userQueryLimit> + <user>alice</user> + <user>bob</user> + <totalCount>3</totalCount> +</userQueryLimit> + </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> +<queue name="root"> + <queue name="queueE"> + <userQueryLimit> + <user>*</user> + <totalCount>2</totalCount> + </userQueryLimit> + </queue> +... + </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> +<queue name="root"> + <userQueryLimit> + <user>userD</user> + <totalCount>2</totalCount> + </userQueryLimit> +... + </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> +<queue name="root"> + <queue name="queueE"> + <groupQueryLimit> + <group>group1</group> + <group>group2</group> + <totalCount>2</totalCount> + </groupQueryLimit> +... + </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> +<queue name="group-set-small"> + <!-- A user not matched by any other rule can run 1 query in the small pool --> + <userQueryLimit> + <user>*</user> + <totalCount>1</totalCount> + </userQueryLimit> + <!-- The user 'alice' can run 4 queries in the small pool --> + <userQueryLimit> + <user>alice</user> + <totalCount>4</totalCount> + </userQueryLimit> +... + </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> +<queue name="group-set-small"> + <!-- A user not matched by any other rule can run 1 query in the small pool --> + <userQueryLimit> + <user>*</user> + <totalCount>1</totalCount> + </userQueryLimit> + <!-- Members of the group 'it' can run 2 queries in the small pool --> + <groupQueryLimit> + <group>it</group> + <totalCount>2</totalCount> + </groupQueryLimit> +... + </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> +<queue name="group-set-small"> + <!-- Members of the group 'it' can run 2 queries in the small pool --> + <groupQueryLimit> + <group>it</group> + <totalCount>2</totalCount> + </groupQueryLimit> + <!-- The user 'fiona' can run 3 queries in the small pool --> + <userQueryLimit> + <user>fiona</user> + <totalCount>3</totalCount> + </userQueryLimit> +... + </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> +<queue name="root"> + <groupQueryLimit> + <group>support</group> + <totalCount>6</totalCount> + </groupQueryLimit> + <queue name="group-set-small"> + <groupQueryLimit> + <group>dev</group> + <group>support</group> + <totalCount>5</totalCount> + </groupQueryLimit> + </queue> +... + </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))
