浏览代码

HUE-4250 [editor] Add configurable timeout for autocompletion API calls

This adds a timeout with default value of 3 seconds when fetching tables, columns, fields, samples and hdfs paths from the API for autocompletion. It will still suggest cached values, from browsing the assist etc. Note that this is only for autocompletion, the assist is not affected by this change.

When the timeout value is set to 0 the autocompleter will never call the API.
Johan Ahlen 9 年之前
父节点
当前提交
2d63fa6

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

@@ -17,6 +17,7 @@
   from desktop.lib.django_util import extract_field_data
   from desktop.views import commonheader, commonfooter, commonshare, _ko
   from beeswax import conf as beeswax_conf
+  from desktop import conf
   from django.utils.translation import ugettext as _
   from notebook.conf import ENABLE_QUERY_BUILDER
 %>
@@ -1224,6 +1225,7 @@ var autocompleter = new Autocompleter({
   user: HIVE_AUTOCOMPLETE_USER,
   oldEditor: true,
   optEnabled: false,
+  timeout: ${ conf.EDITOR_AUTOCOMPLETE_TIMEOUT.get() },
   useNewAutocompleter: false
 });
 

+ 4 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -147,6 +147,10 @@
   # Choose whether to show the new SQL editor.
   ## use_new_editor=true
 
+  # Editor autocomplete timeout (ms) when fetching columns, fields, tables etc.
+  # To disable this type of autocompletion set the value to 0
+  ## editor_autocomplete_timeout=3000
+
   # Enable saved default configurations for Hive, Impala, Spark, and Oozie.
   ## use_default_configuration=false
 

+ 8 - 1
desktop/core/src/desktop/conf.py

@@ -1101,13 +1101,20 @@ DJANGO_EMAIL_BACKEND = Config(
   default="django.core.mail.backends.smtp.EmailBackend"
 )
 
-USE_NEW_AUTOCOMPLETER = Config( # To remove when it's working properly
+USE_NEW_AUTOCOMPLETER = Config( # To remove when it's working properly, not supported by old editor
   key='use_new_autocompleter',
   default=False,
   type=coerce_bool,
   help=_('Enable the new editor SQL autocompleter')
 )
 
+EDITOR_AUTOCOMPLETE_TIMEOUT = Config(
+  key='editor_autocomplete_timeout',
+  type=int,
+  default=3000,
+  help=_('Timeout value in ms for autocomplete of columns, tables, values etc. 0 = disabled')
+)
+
 USE_NEW_EDITOR = Config( # To remove in Hue 4
   key='use_new_editor',
   default=True,

+ 52 - 22
desktop/core/src/desktop/static/desktop/js/apiHelper.js

@@ -235,6 +235,7 @@
    * @param {Function} options.successCallback
    * @param {Function} [options.errorCallback]
    * @param {boolean} [options.silenceErrors]
+   * @param {Number} [options.timeout]
    * @param {Object} [options.editor] - Ace editor
    *
    * @param {string[]} options.pathParts
@@ -244,9 +245,14 @@
     var url = HDFS_API_PREFIX + "/" + options.pathParts.join("/") + HDFS_PARAMETERS;
 
     var fetchFunction = function (storeInCache) {
+      if (options.timeout === 0) {
+        self.assistErrorCallback(options)({ status: -1 });
+        return;
+      }
       $.ajax({
         dataType: "json",
         url: url,
+        timeout: options.timeout,
         success: function (data) {
           if (!data.error && !self.successResponseIsError(data) && typeof data.files !== 'undefined' && data.files !== null) {
             if (data.files.length > 2) {
@@ -673,6 +679,7 @@
    *
    * @param {string} options.databaseName
    * @param {string} options.tableName
+   * @param {Number} [options.timeout]
    * @param {string} [options.columnName]
    * @param {Object} [options.editor] - Ace editor
    */
@@ -681,13 +688,21 @@
     var url = SAMPLE_API_PREFIX + options.databaseName + '/' + options.tableName + (options.columnName ? '/' + options.columnName : '');
 
     var fetchFunction = function (storeInCache) {
-      $.post(url, {
-        notebook: {},
-        snippet: ko.mapping.toJSON({
-          type: options.sourceType
-        })
-      })
-      .done(function (data) {
+      if (options.timeout === 0) {
+        self.assistErrorCallback(options)({ status: -1 });
+        return;
+      }
+      $.ajax({
+        type: 'POST',
+        url: url,
+        data: {
+          notebook: {},
+          snippet: ko.mapping.toJSON({
+            type: options.sourceType
+          })
+        },
+        timeout: options.timeout
+      }).done(function (data) {
         if (! self.successResponseIsError(data)) {
           if ((typeof data.rows !== 'undefined' && data.rows.length > 0) || typeof data.sample !== 'undefined') {
             storeInCache(data);
@@ -821,6 +836,7 @@
    * @param {Function} options.successCallback
    * @param {Function} [options.errorCallback]
    * @param {boolean} [options.silenceErrors]
+   * @param {Number} [options.timeout]
    * @param {Object} [options.editor] - Ace editor
    *
    * @param {string} options.databaseName
@@ -854,6 +870,7 @@
    * @param {Function} options.successCallback
    * @param {Function} [options.errorCallback]
    * @param {boolean} [options.silenceErrors]
+   * @param {Number} [options.timeout]
    * @param {Object} [options.editor] - Ace editor
    *
    * @param {string} options.databaseName
@@ -895,6 +912,7 @@
    * @param {Function} options.cacheCondition - Determines whether it should be cached or not
    * @param {Function} options.successCallback
    * @param {Function} options.errorCallback
+   * @param {Number} [options.timeout]
    * @param {Object} [options.editor] - Ace editor
    */
   var fetchAssistData = function (options) {
@@ -918,12 +936,32 @@
       return;
     }
 
-    $.post(options.url, {
-      notebook: {},
-      snippet: ko.mapping.toJSON({
-        type: options.sourceType
-      })
-    }, function (data) {
+    var failCallback = function (data) {
+      while (queue.length > 0) {
+        var next = queue.shift();
+        next.errorCallback(data);
+        if (typeof next.editor !== 'undefined' && next.editor !== null) {
+          next.editor.hideSpinner();
+        }
+      }
+    };
+
+    if (options.timeout === 0) {
+      failCallback({ status: -1 });
+      return;
+    }
+
+    $.ajax({
+      type: 'POST',
+      url: options.url,
+      data: {
+        notebook: {},
+        snippet: ko.mapping.toJSON({
+          type: options.sourceType
+        })
+      },
+      timeout: options.timeout
+    }).success(function (data) {
       // Safe to assume all requests in the queue have the same cacheCondition
       if (data.status === 0 && !self.successResponseIsError(data) && options.cacheCondition(data)) {
         cachedData[options.url] = {
@@ -943,15 +981,7 @@
           next.editor.hideSpinner();
         }
       }
-    }).fail(function (data) {
-      while (queue.length > 0) {
-        var next = queue.shift();
-        next.errorCallback(data);
-        if (typeof next.editor !== 'undefined' && next.editor !== null) {
-          next.editor.hideSpinner();
-        }
-      }
-    });
+    }).fail(failCallback);
   };
 
   /**

+ 9 - 4
desktop/core/src/desktop/static/desktop/js/autocompleter.js

@@ -27,35 +27,40 @@
 }(this, function (SqlAutocompleter, SqlAutocompleter2, HdfsAutocompleter) {
 
   /**
-   * @param options {object}
+   * @param {Object} options {object}
    * @param options.snippet
    * @param options.user
    * @param options.optEnabled
+   * @param {Number} options.timeout
    * @param options.useNewSqlAutocompleter {boolean}
    * @constructor
    */
   function Autocompleter(options) {
     var self = this;
     self.snippet = options.snippet;
+    self.timeout = options.timeout;
     
     self.topTables = {};
 
     var initializeAutocompleter = function () {
       if (self.snippet.isSqlDialect() && options.useNewAutocompleter) {
         self.autocompleter = new SqlAutocompleter2({
-          snippet: self.snippet
+          snippet: self.snippet,
+          timeout: self.timeout
         });
       } else {
         var hdfsAutocompleter = new HdfsAutocompleter({
           user: options.user,
-          snippet: options.snippet
+          snippet: options.snippet,
+          timeout: options.timeout
         });
         if (self.snippet.isSqlDialect()) {
           self.autocompleter = new SqlAutocompleter({
             hdfsAutocompleter: hdfsAutocompleter,
             snippet: options.snippet,
             oldEditor: options.oldEditor,
-            optEnabled: options.optEnabled
+            optEnabled: options.optEnabled,
+            timeout: self.timeout
           })
         } else {
           self.autocompleter = hdfsAutocompleter;

+ 3 - 0
desktop/core/src/desktop/static/desktop/js/hdfsAutocompleter.js

@@ -29,6 +29,7 @@
   /**
    * @param {object} options
    * @param {string} options.user
+   * @param {Number} options.timeout
    * @param {Snippet} options.snippet
    *
    * @constructor
@@ -37,6 +38,7 @@
     var self = this;
     self.user = options.user;
     self.snippet = options.snippet;
+    self.timeout = options.timeout
   }
 
   HdfsAutocompleter.prototype.getTotalStorageUserPrefix = function () {
@@ -100,6 +102,7 @@
         successCallback: successCallback,
         silenceErrors: true,
         errorCallback: onFailure,
+        timeout: self.timeout,
         editor: editor
       });
     } else {

+ 12 - 1
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter.js

@@ -27,11 +27,13 @@
   /**
    * @param {Object} options
    * @param {Snippet} options.snippet
+   * @param {Number} options.timeout
    * @param {HdfsAutocompleter} options.hdfsAutocompleter
    * @constructor
    */
   function SqlAutocompleter(options) {
     var self = this;
+    self.timeout = options.timeout;
     self.snippet = options.snippet;
     self.hdfsAutocompleter = options.hdfsAutocompleter;
     self.oldEditor = options.oldEditor || false;
@@ -262,6 +264,7 @@
             tableName: tableName,
             fields: completeFields,
             editor: editor,
+            timeout: self.timeout,
             successCallback: function (data) {
               if (data.type === "map") {
                 completeFields.push("value");
@@ -298,6 +301,8 @@
           tableName: tableName,
           columnName: fields.length === 1 ? fields[0] : null,
           editor: editor,
+          timeout: self.timeout,
+          silenceErrors: true,
           successCallback: function (data) {
             if (data.status === 0 && data.headers.length === 1) {
               var values = $.map(data.rows, function (row, index) {
@@ -321,6 +326,8 @@
           errorCallback: function () {
             if (self.snippet.type() === 'impala') {
               fetchImpalaFields(fields, []);
+            } else {
+              callback([]);
             }
           }
         });
@@ -609,7 +616,8 @@
         },
         silenceErrors: true,
         errorCallback: onFailure,
-        editor: editor
+        editor: editor,
+        timeout: self.timeout
       });
       return;
     } else if ((selectBefore && fromAfter) || fieldTermBefore || impalaFieldRef) {
@@ -711,6 +719,7 @@
             tableName: tableName,
             fields: fields,
             editor: editor,
+            timeout: self.timeout,
             successCallback: function (data) {
               var suggestions = [];
               if (fields.length == 0) {
@@ -788,6 +797,7 @@
                 tableName: tableName,
                 fields: fields,
                 editor: editor,
+                timeout: self.timeout,
                 successCallback: function(data) {
                   if (data.type === "map") {
                     fields.push("value");
@@ -849,6 +859,7 @@
               tableName: tableName,
               fields: fields,
               editor: editor,
+              timeout: self.timeout,
               successCallback: successCallback,
               silenceErrors: true,
               errorCallback: onFailure

+ 9 - 2
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter2.js

@@ -28,11 +28,13 @@
   /**
    * @param {Object} options
    * @param {Snippet} options.snippet
+   * @param {Number} options.timeout
    * @constructor
    */
   function SqlAutocompleter2(options) {
     var self = this;
     self.snippet = options.snippet;
+    self.timeout = options.timeout;
   }
 
   SqlAutocompleter2.prototype.autocomplete = function(beforeCursor, afterCursor, callback, editor) {
@@ -105,7 +107,8 @@
           },
           silenceErrors: true,
           errorCallback: hdfsDeferred.resolve,
-          editor: editor
+          editor: editor,
+          timeout: self.timeout
         });
       }
 
@@ -128,7 +131,8 @@
           },
           silenceErrors: true,
           errorCallback: tableDeferred.resolve,
-          editor: editor
+          editor: editor,
+          timeout: self.timeout
         });
       }
 
@@ -153,6 +157,7 @@
           tableName: parseResult.suggestColumns.table,
           fields: fields,
           editor: editor,
+          timeout: self.timeout,
           successCallback: function (data) {
             if (data.extended_columns) {
               data.extended_columns.forEach(function (column) {
@@ -215,6 +220,7 @@
           tableName: parseResult.suggestValues.table,
           columnName: parseResult.suggestValues.identifierChain[0].name,
           editor: editor,
+          timeout: self.timeout,
           successCallback: function (data) {
             if (data.status === 0 && data.headers.length === 1) {
               data.rows.forEach(function (row) {
@@ -238,6 +244,7 @@
             tableName: parseResult.suggestValues.table,
             fields: $.map(parseResult.suggestValues.identifierChain, function (value) { return value.name }),
             editor: editor,
+            timeout: self.timeout,
             successCallback: function (data) {
               if (data.sample) {
                 var isString = data.type === "string";

+ 2 - 0
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -1134,6 +1134,7 @@
       snippet: self,
       user: vm.user,
       optEnabled: false,
+      timeout: vm.autocompleteTimeout,
       useNewAutocompleter: vm.useNewAutocompleter
     });
 
@@ -1783,6 +1784,7 @@
     });
     self.editorTypeTitle = ko.observable(options.editor_type);
     self.useNewAutocompleter = options.useNewAutocompleter || false;
+    self.autocompleteTimeout = options.autocompleteTimeout;
     self.selectedNotebook = ko.observable();
     self.combinedContent = ko.observable();
     self.isPlayerMode = ko.observable(false);

+ 1 - 0
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -2586,6 +2586,7 @@ ${ hueIcons.symbols() }
       %if conf.USE_NEW_AUTOCOMPLETER.get():
       useNewAutocompleter: true,
       %endif
+      autocompleteTimeout: ${ conf.EDITOR_AUTOCOMPLETE_TIMEOUT.get() },
       snippetViewSettings: {
         default: {
           placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }',