浏览代码

HUE-1211 [impala] Cancel a query

Only Impala support query cancelation for now with HS2 interface.
Make Hive Server 2 interface default for Impala.
Cancelation code is in Beeswax as Hive Server 2 will implement it at some point.
Romain Rigaux 12 年之前
父节点
当前提交
8fe78f8

+ 5 - 0
apps/beeswax/src/beeswax/server/beeswax_lib.py

@@ -20,6 +20,7 @@ import re
 import thrift
 
 from django.utils.encoding import smart_str, force_unicode
+from django.utils.translation import ugettext as _
 
 import hadoop.cluster
 
@@ -160,6 +161,10 @@ class BeeswaxClient:
       return BeeswaxDataTable(results)
 
 
+  def cancel_operation(self, handle):
+    raise Exception(_('Query cancelation is not supported by the Beeswax interface. Please use the Hive Server 2 interface instead.'))
+
+
   def get_log(self, handle):
     return self.db_client.get_log(handle.log_context)
 

+ 4 - 0
apps/beeswax/src/beeswax/server/dbms.py

@@ -129,6 +129,10 @@ class Dbms:
     return self.client.fetch(query_handle, start_over, rows)
 
 
+  def cancel_operation(self, query_handle):
+    return self.client.cancel_operation(query_handle)
+
+
   def get_sample(self, database, table):
     """No samples if it's a view (HUE-526)"""
     if not table.is_view:

+ 11 - 1
apps/beeswax/src/beeswax/server/hive_server2_lib.py

@@ -25,7 +25,7 @@ from TCLIService import TCLIService
 from TCLIService.ttypes import TOpenSessionReq, TGetTablesReq, TFetchResultsReq,\
   TStatusCode, TGetResultSetMetadataReq, TGetColumnsReq, TType,\
   TExecuteStatementReq, TGetOperationStatusReq, TFetchOrientation,\
-  TCloseSessionReq, TGetSchemasReq, TGetLogReq
+  TCloseSessionReq, TGetSchemasReq, TGetLogReq, TCancelOperationReq
 
 from beeswax import conf
 from beeswax.models import Session, HiveServerQueryHandle, HiveServerQueryHistory
@@ -413,6 +413,11 @@ class HiveServerClient:
     return HiveServerDataTable(results, schema)
 
 
+  def cancel_operation(self, operation_handle):
+    req = TCancelOperationReq(operationHandle=operation_handle)
+    return self.call(self._client.CancelOperation, req)
+
+
   def get_columns(self, database, table):
     req = TGetColumnsReq(schemaName=database, tableName=table)
     res = self.call(self._client.GetColumns, req)
@@ -550,6 +555,11 @@ class HiveServerClientCompatible:
     return ResultCompatible(data_table)
 
 
+  def cancel_operation(self, handle):
+    operationHandle = handle.get_rpc_handle()
+    return self._client.cancel_operation(operationHandle)
+
+
   def dump_config(self):
     return 'Does not exist in HS2'
 

+ 1 - 2
apps/beeswax/src/beeswax/templates/execute.mako

@@ -224,7 +224,7 @@ ${layout.menubar(section='query')}
                           </li>
                           <li>
                             <div class="control-group">
-                              <button id="refresh-btn" class="btn btn-small" data-loading-text="Refreshing..." rel="tooltip" data-placement="right" data-original-title="${ _('Update the list of tables seen by Impala. It can take a few seconds...') }">
+                              <button id="refresh-btn" class="btn btn-small" data-loading-text="${ _('Refreshing...') }" rel="tooltip" data-placement="right" data-original-title="${ _('Update the list of tables seen by Impala. It can take a few seconds...') }">
                                 ${ _('Refresh') }
                               </button>
                             </div>
@@ -436,7 +436,6 @@ ${layout.menubar(section='query')}
     color: #666;
     font-style: normal;
   }
-
 </style>
 
 <script src="/static/ext/js/jquery/plugins/jquery-fieldselection.js" type="text/javascript"></script>

+ 66 - 42
apps/beeswax/src/beeswax/templates/watch_wait.mako

@@ -28,47 +28,56 @@ ${layout.menubar(section='query')}
 <!-- <meta http-equiv="refresh" content="3;${url(app_name + ':watch_query', query.id)}?${fwd_params}" /> -->
 
 <div class="container-fluid">
-	<h1>${_('Waiting for query...')} ${util.render_query_context(query_context)}</h1>
-	<div class="row-fluid">
-		<div class="span3">
-			<div class="well sidebar-nav
-			    % if app_name == 'impala':
-			      hide
-			    % endif
-			">
-				<ul class="nav nav-list">
-					<%
-			          n_jobs = hadoop_jobs and len(hadoop_jobs) or 0
-			          mr_jobs = (n_jobs == 1) and _('MR Job') or _('MR Jobs')
-			        %>
-				 	% if n_jobs > 0:
-						<li id="jobsHeader" class="nav-header">${mr_jobs} (${n_jobs})</li>
-						% for jobid in hadoop_jobs:
-						    <li><a class="jobLink" href="${url("jobbrowser.views.single_job", job=jobid)}">${jobid.replace("job_", "")}</a></li>
-						% endfor
-					% else:
-						<li id="jobsHeader" class="nav-header">${mr_jobs}</li>
-						<li class="jobLink">${_('No Hadoop jobs were launched in running this query.')}</li>
-					% endif
-				</ul>
-			</div>
-		</div>
-		<div class="span9">
-			<ul class="nav nav-tabs">
-				<li class="active"><a href="#log" data-toggle="tab">${_('Log')}</a></li>
-				<li><a href="#query" data-toggle="tab">${_('Query')}</a></li>
-			</ul>
-
-		   	<div class="tab-content">
-				<div class="active tab-pane" id="log">
-					<pre>${ log }</pre>
-				</div>
-				<div class="tab-pane" id="query">
-					<pre>${ query.get_current_statement() }</pre>
-				</div>
-			</div>
-		</div>
-	</div>
+    <h1>${_('Waiting for query...')} ${util.render_query_context(query_context)}</h1>
+    <div class="row-fluid">
+        <div class="span3">
+            % if app_name == 'impala':
+              <li class="nav">
+                <div class="control-group">
+                  <button id="cancel-btn" class="btn btn-small" data-loading-text="${ _('Canceling...') }" rel="tooltip" data-placement="right" data-original-title="${ _('Cancel the query') }">
+                    ${ _('Cancel') }
+                  </button>
+                </div>
+              </li>
+            % endif
+            <div class="well sidebar-nav
+                % if app_name == 'impala':
+                  hide
+                % endif
+            ">
+                <ul class="nav nav-list">
+                    <%
+                      n_jobs = hadoop_jobs and len(hadoop_jobs) or 0
+                      mr_jobs = (n_jobs == 1) and _('MR Job') or _('MR Jobs')
+                    %>
+                     % if n_jobs > 0:
+                        <li id="jobsHeader" class="nav-header">${mr_jobs} (${n_jobs})</li>
+                        % for jobid in hadoop_jobs:
+                            <li><a class="jobLink" href="${url("jobbrowser.views.single_job", job=jobid)}">${jobid.replace("job_", "")}</a></li>
+                        % endfor
+                    % else:
+                        <li id="jobsHeader" class="nav-header">${mr_jobs}</li>
+                        <li class="jobLink">${_('No Hadoop jobs were launched in running this query.')}</li>
+                    % endif
+                </ul>
+            </div>
+        </div>
+        <div class="span9">
+            <ul class="nav nav-tabs">
+                <li class="active"><a href="#log" data-toggle="tab">${_('Log')}</a></li>
+                <li><a href="#query" data-toggle="tab">${_('Query')}</a></li>
+            </ul>
+
+               <div class="tab-content">
+                <div class="active tab-pane" id="log">
+                    <pre>${ log }</pre>
+                </div>
+                <div class="tab-pane" id="query">
+                    <pre>${ query.get_current_statement() }</pre>
+                </div>
+            </div>
+        </div>
+    </div>
 </div>
 
 <script>
@@ -131,8 +140,23 @@ ${layout.menubar(section='query')}
       $("#log pre").css("overflow", "auto").height($(window).height() - $("#log pre").position().top - 40);
     }
 
+    % if app_name == 'impala':
+      $("#cancel-btn").click(function() {
+        var _this = this;
+        $(_this).button('loading');
+        $.post("${ url(app_name + ':cancel_operation', query.id) }",
+          function(response) {
+            if (response['status'] != 0) {
+              $.jHueNotify.error("${ _('Problem: ') }" + response['message']);
+            } else {
+              $.jHueNotify.info("${ _('Query canceled!') }")
+            }
+          }
+        );
+        return false;
+      });
+    % endif
   });
 </script>
 
-
 ${ commonfooter(messages) | n,unicode }

+ 1 - 0
apps/beeswax/src/beeswax/urls.py

@@ -25,6 +25,7 @@ urlpatterns = patterns('beeswax.views',
   url(r'^execute_parameterized/(?P<design_id>\d+)$', 'execute_parameterized_query', name='execute_parameterized_query'),
   url(r'^watch/(?P<id>\d+)$', 'watch_query', name='watch_query'),
   url(r'^watch/json/(?P<id>\d+)$', 'watch_query_refresh_json', name='watch_query_refresh_json'),
+  url(r'^cancel_operation/(?P<query_id>\d+)?$', 'cancel_operation', name='cancel_operation'),
   url(r'^results/(?P<id>\d+)/(?P<first_row>\d+)$', 'view_results', name='view_results'),
   url(r'^download/(?P<id>\d+)/(?P<format>\w+)$', 'download', name='download'),
   url(r'^save_results/(?P<id>\d+)$', 'save_results', name='save_results'),

+ 18 - 0
apps/beeswax/src/beeswax/views.py

@@ -547,6 +547,24 @@ def watch_query_refresh_json(request, id):
   return HttpResponse(json.dumps(result), mimetype="application/json")
 
 
+def cancel_operation(request, query_id):
+  response = {'status': -1, 'message': ''}
+
+  if request.method != 'POST':
+    response['message'] = _('A POST request is required.')
+  else:
+    try:
+      query_history = authorized_get_history(request, query_id, must_exist=True)
+      db = dbms.get(request.user, query_history.get_query_server_config())
+      db.cancel_operation(query_history.get_handle())
+      _get_query_handle_and_state(query_history)
+      response = {'status': 0}
+    except Exception, e:
+      response = {'message': unicode(e)}
+
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+
 def view_results(request, id, first_row=0):
   """
   Returns the view for the results of the QueryHistory with the given id.

+ 3 - 4
apps/impala/src/impala/conf.py

@@ -29,14 +29,13 @@ SERVER_HOST = Config(
 SERVER_PORT = Config(
   key="server_port",
   help=_("Port of the Impala Server."),
-  default=21000,
+  default=21050,
   type=int)
 
 SERVER_INTERFACE = Config(
   key="server_interface",
-  help=_("Beeswax or Hive Server 2 Thrift API used. Choices are: 'beeswax' or 'hiveserver2'."
-         "'beeswax' requires Beeswax to run for proxying the metadata requests"),
-  default="beeswax")
+  help=_("Beeswax or Hive Server 2 Thrift API used. Choices are: 'beeswax' or 'hiveserver2'."),
+  default="hiveserver2")
 
 IMPALA_PRINCIPAL=Config(
   key='impala_principal',