AMBARI-20253. HiveView2.0: Auto refresh not working.(dipayanb)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/dff7754b Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/dff7754b Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/dff7754b Branch: refs/heads/branch-dev-logsearch Commit: dff7754b7cb718de7e3bcfaac7afa0a4ce936afe Parents: a92b7c8 Author: Dipayan Bhowmick <dipayan.bhowm...@gmail.com> Authored: Wed Mar 8 01:02:16 2017 +0530 Committer: Dipayan Bhowmick <dipayan.bhowm...@gmail.com> Committed: Wed Mar 8 01:02:16 2017 +0530 ---------------------------------------------------------------------- .../main/resources/ui/app/routes/databases.js | 23 +++ .../ui/app/routes/databases/database/tables.js | 57 ++++++- .../app/routes/databases/database/tables/new.js | 18 ++- .../resources/ui/app/services/auto-refresh.js | 148 +++++++++++++++++++ .../app/templates/databases/database/tables.hbs | 1 + .../main/resources/ui/app/transforms/date.js | 50 +++++++ .../src/main/resources/ui/config/environment.js | 6 +- 7 files changed, 289 insertions(+), 14 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/dff7754b/contrib/views/hive20/src/main/resources/ui/app/routes/databases.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/routes/databases.js b/contrib/views/hive20/src/main/resources/ui/app/routes/databases.js index e5efbe9..02dbcac 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/routes/databases.js +++ b/contrib/views/hive20/src/main/resources/ui/app/routes/databases.js @@ -18,9 +18,32 @@ import Ember from 'ember'; import UILoggerMixin from '../mixins/ui-logger'; +import ENV from 'ui/config/environment'; export default Ember.Route.extend(UILoggerMixin, { tableOperations: Ember.inject.service(), + autoRefresh: Ember.inject.service(), + + activate() { + if(ENV.APP.SHOULD_AUTO_REFRESH_DATABASES) { + this.get('autoRefresh').startDatabasesAutoRefresh(() => { + console.log("Databases AutoRefresh started"); + }, this._databasesRefreshed.bind(this)); + } + + }, + + deactivate() { + this.get('autoRefresh').stopDatabasesAutoRefresh(); + }, + + _databasesRefreshed() { + let model = this.store.peekAll('database'); + if(this.controller) { + console.log(model.get('length')); + this.setupController(this.controller, model); + } + }, model() { return this.store.findAll('database', {reload: true}); http://git-wip-us.apache.org/repos/asf/ambari/blob/dff7754b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables.js b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables.js index d4a39cb..f5940e3 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables.js +++ b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables.js @@ -17,8 +17,46 @@ */ import Ember from 'ember'; +import ENV from 'ui/config/environment'; +import UILoggerMixin from '../../../mixins/ui-logger'; + +export default Ember.Route.extend(UILoggerMixin, { + autoRefresh: Ember.inject.service(), + + activate() { + if(ENV.APP.SHOULD_AUTO_REFRESH_TABLES) { + let selectedDatabase = this.modelFor('databases.database'); + this.get('autoRefresh').startTablesAutoRefresh(selectedDatabase.get('name'), + this.tableRefreshStarting.bind(this), this.tableRefreshed.bind(this)); + } + }, + + deactivate() { + this.get('autoRefresh').stopTablesAutoRefresh(this.controller.get('database.name')); + }, + + tableRefreshStarting(databaseName) { + this.controller.set('tableRefreshing', true); + }, + + tableRefreshed(databaseName, deletedTablesCount) { + this.controller.set('tableRefreshing', false); + let currentTablesForDatabase = this.store.peekAll('table').filterBy('database.name', databaseName); + let paramsForTable = this.paramsFor('databases.database.tables.table'); + let currentTableNamesForDatabase = currentTablesForDatabase.mapBy('name'); + if (currentTableNamesForDatabase.length <= 0 || !currentTableNamesForDatabase.contains(paramsForTable.name)) { + if(deletedTablesCount !== 0) { + this.get('logger').info(`Current selected table '${paramsForTable.name}' has been deleted from Hive Server. Transitioning out.`); + this.transitionTo('databases.database', databaseName); + return; + } + } + if(currentTablesForDatabase.get('length') > 0) { + this.selectTable(currentTablesForDatabase); + this.controller.set('model', currentTablesForDatabase); + } + }, -export default Ember.Route.extend({ model() { let selectedDatabase = this.modelFor('databases.database'); return this.store.query('table', {databaseId: selectedDatabase.get('name')}); @@ -44,6 +82,13 @@ export default Ember.Route.extend({ toSelect.set('selected', true); } }, + + setupController(controller, model) { + this._super(...arguments); + let selectedDatabase = this.modelFor('databases.database'); + controller.set('database', selectedDatabase); + }, + actions: { tableSelected(table) { let tables = this.controllerFor('databases.database.tables').get('model'); @@ -52,11 +97,11 @@ export default Ember.Route.extend({ }); table.set('selected', true); this.transitionTo('databases.database.tables.table', table.get('name')); + }, + + refreshTable() { + let databaseName = this.controller.get('database.name'); + this.get('autoRefresh').refreshTables(databaseName, this.tableRefreshStarting.bind(this), this.tableRefreshed.bind(this), true); } } - - - - - }); http://git-wip-us.apache.org/repos/asf/ambari/blob/dff7754b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/new.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/new.js b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/new.js index 533dd61..b61008f 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/new.js +++ b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/new.js @@ -95,13 +95,17 @@ export default Ember.Route.extend(UILoggerMixin, { }, _addTableToStoreLocally(database, table) { - this.store.createRecord('table', { - id: `${database.get('name')}/${table}`, - name: `${table}`, - type: 'TABLE', - selected: true, - database: database - }); + // Add only if it has not been added by the auto refresh + let existingRecord = this.store.peekRecord('table', `${database.get('name')}/${table}`); + if(Ember.isEmpty(existingRecord)) { + this.store.createRecord('table', { + id: `${database.get('name')}/${table}`, + name: `${table}`, + type: 'TABLE', + selected: true, + database: database + }); + } }, _resetModelInTablesController(tables) { http://git-wip-us.apache.org/repos/asf/ambari/blob/dff7754b/contrib/views/hive20/src/main/resources/ui/app/services/auto-refresh.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/services/auto-refresh.js b/contrib/views/hive20/src/main/resources/ui/app/services/auto-refresh.js new file mode 100644 index 0000000..2e8ec62 --- /dev/null +++ b/contrib/views/hive20/src/main/resources/ui/app/services/auto-refresh.js @@ -0,0 +1,148 @@ +/** + * 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 + * + * http://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. + */ + +import Ember from 'ember'; + +export default Ember.Service.extend({ + store: Ember.inject.service(), + isDatabaseRefresherRunning: false, + tablesRefresherRunningStatus: {}, + + + startDatabasesAutoRefresh(databaseRefreshStartingCallback, databaseRefreshedCallback, startAfter = 30 * 1000, interval = 30 * 1000) { + if (this.get('isDatabaseRefresherRunning')) { + return; + } + + console.log("Starting database auto refresh"); + + this.set('isDatabaseRefresherRunning', true); + Ember.run.later(() => { + this._refreshDatabases(databaseRefreshStartingCallback, databaseRefreshedCallback, interval); + }, startAfter); + + + }, + + _refreshDatabases(databaseRefreshStartingCallback, databaseRefreshedCallback, interval) { + let reRun = () => { + Ember.run.later(() => { + this._refreshDatabases(databaseRefreshStartingCallback, databaseRefreshedCallback, interval); + }, interval); + }; + + if (this.get('isDatabaseRefresherRunning')) { + databaseRefreshStartingCallback(); + let oldDatabases = this.get('store').peekAll('database').mapBy('name'); + this.get('store').query('database', {}).then((data) => { + let deletedDbCount = 0 + let newDatabases = data.mapBy('name'); + oldDatabases.forEach((oldDB) => { + if (!newDatabases.contains(oldDB)) { + deletedDbCount++; + let oldRecord = this.get('store').peekRecord('database', oldDB); + this.get('store').unloadRecord(oldRecord); + } + }); + + // Hack: Had to wrap the refreshed call inside run later because, unloadRecord is not synchronously unloading + // records from store. + Ember.run.later(() => databaseRefreshedCallback(deletedDbCount)); + reRun(); + }).catch((err) => { + reRun(); + }); + } + }, + + stopDatabasesAutoRefresh() { + console.log("Stopping database auto refresh"); + this.set('isDatabaseRefresherRunning', false); + }, + + startTablesAutoRefresh(databaseName, tablesRefreshStartingCallback, tablesRefreshedCallback, startAfter = 15 * 1000, interval = 15 * 1000) { + if(!Ember.isEmpty(this.get('tablesRefresherRunningStatus')[databaseName])) { + if (this.get('tablesRefresherRunningStatus')[databaseName]["started"]) { + return; + } + } + + + console.log("Starting tables auto refresh for " + databaseName); + + this.get('tablesRefresherRunningStatus')[databaseName] = {}; + this.get('tablesRefresherRunningStatus')[databaseName]["started"] = true; + Ember.run.later(() => { + this.refreshTables(databaseName, tablesRefreshStartingCallback, tablesRefreshedCallback, false, interval); + }, startAfter); + }, + + refreshTables(databaseName, tablesRefreshStartingCallback, tablesRefreshedCallback, runOnce = false, interval) { + let reRun = () => { + let intervalRef = Ember.run.later(() => { + this.refreshTables(databaseName, tablesRefreshStartingCallback, tablesRefreshedCallback, false, interval); + }, interval); + this.get('tablesRefresherRunningStatus')[databaseName]["intervalRef"] = intervalRef; + }; + + if (this.get('tablesRefresherRunningStatus')[databaseName]) { + tablesRefreshStartingCallback(databaseName); + let oldTableNames = this.get('store').peekAll('table').filterBy('database.name', databaseName).mapBy('name'); + this.get('store').query('table', {databaseId: databaseName}).then((data) => { + let deletedTablesCount = 0; + let newTableNames = data.mapBy('name'); + oldTableNames.forEach((oldTable) => { + if (!newTableNames.contains(oldTable)) { + deletedTablesCount++; + let oldRecord = this.get('store').peekRecord('table', `${databaseName}/${oldTable}`); + this.get('store').unloadRecord(oldRecord); + } + }); + + newTableNames.forEach((newTable) => { + if(!oldTableNames.contains(newTable)) { + //table has been added + let tableRecord = this.get('store').peekRecord('table', `${databaseName}/${newTable}`); + let dbRecord = this.get('store').peekRecord('database', databaseName); + dbRecord.get('tables').pushObject(tableRecord); + } + }); + + // Hack: Had to wrap the refreshed call inside run later because, unloadRecord is not synchronously unloading + // records from store. + Ember.run.later(() => tablesRefreshedCallback(databaseName, deletedTablesCount)); + if(!runOnce) { + reRun(); + } + }).catch((err) => { + if(!runOnce) { + reRun(); + } + }); + } + }, + + stopTablesAutoRefresh(databaseName) { + console.log("Stopping tables auto refresh for " + databaseName); + this.get('tablesRefresherRunningStatus')[databaseName]["started"] = false; + let intervalRef = this.get('tablesRefresherRunningStatus')[databaseName]["intervalRef"]; + if (intervalRef) { + Ember.run.cancel(intervalRef); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/dff7754b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables.hbs b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables.hbs index c2d845b..954725b 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables.hbs +++ b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables.hbs @@ -20,6 +20,7 @@ <div class="col-md-3"> <div class="row"> <div class="hv-dropdown tables-dropdown"> + <a href="#" {{action "refreshTable"}} class="btn btn-default">{{fa-icon "refresh" spin=tableRefreshing}}</a> {{#link-to "databases.database.tables.new" class="btn btn-success"}}{{fa-icon "plus"}}{{/link-to}} </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/dff7754b/contrib/views/hive20/src/main/resources/ui/app/transforms/date.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/transforms/date.js b/contrib/views/hive20/src/main/resources/ui/app/transforms/date.js new file mode 100644 index 0000000..40804d5 --- /dev/null +++ b/contrib/views/hive20/src/main/resources/ui/app/transforms/date.js @@ -0,0 +1,50 @@ +/** + * 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 + * + * http://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. + */ + +import Ember from 'ember'; +import DS from 'ember-data'; + +export default DS.Transform.extend({ + + deserialize: function (serialized) { + var type = typeof serialized; + + if (type === "string") { + return new Date(Ember.Date.parse(serialized)); + } else if (type === "number") { + return new Date(serialized); + } else if (serialized === null || serialized === undefined) { + // if the value is not present in the data, + // return undefined, not null. + return serialized; + } else { + return null; + } + }, + + serialize: function (date) { + console.log("Coming here to serialize"); + if (date instanceof Date) { + // Serialize it as a number to maintain millisecond precision + return Number(date); + } else { + return null; + } + } + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/dff7754b/contrib/views/hive20/src/main/resources/ui/config/environment.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/config/environment.js b/contrib/views/hive20/src/main/resources/ui/config/environment.js index 411ee99..c5370d7 100644 --- a/contrib/views/hive20/src/main/resources/ui/config/environment.js +++ b/contrib/views/hive20/src/main/resources/ui/config/environment.js @@ -34,7 +34,9 @@ module.exports = function(environment) { APP: { // Here you can pass flags/options to your application instance // when it is created - SHOULD_PERFORM_SERVICE_CHECK: true + SHOULD_PERFORM_SERVICE_CHECK: true, + SHOULD_AUTO_REFRESH_TABLES: true, + SHOULD_AUTO_REFRESH_DATABASES: true } }; @@ -49,6 +51,8 @@ module.exports = function(environment) { // Change the value to false to prevent the service checks. This is required in development mode // as service checks take up time and hence increase the overall development time. ENV.APP.SHOULD_PERFORM_SERVICE_CHECK = false; + ENV.APP.SHOULD_AUTO_REFRESH_TABLES = false; + ENV.APP.SHOULD_AUTO_REFRESH_DATABASES = false; } if (environment === 'test') {