Repository: ambari Updated Branches: refs/heads/trunk ca09c208e -> dbb356774
AMBARI-19939 Service Config pages load very slowly Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/dbb35677 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/dbb35677 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/dbb35677 Branch: refs/heads/trunk Commit: dbb3567740c9a2f5d48baab390c7f10711380f98 Parents: ca09c20 Author: Denys Buzhor <bde...@hortonworks.com> Authored: Thu Feb 9 15:55:18 2017 +0200 Committer: Denys Buzhor <bde...@hortonworks.com> Committed: Thu Feb 9 19:03:05 2017 +0200 ---------------------------------------------------------------------- .../controllers/main/service/info/configs.js | 13 ++- .../app/mixins/common/configs/configs_loader.js | 2 +- .../app/mixins/common/track_request_mixin.js | 36 +++++++- ambari-web/app/routes/main.js | 15 +++- .../main/host/configs_service_test.js | 2 + .../test/controllers/main/host/details_test.js | 1 + .../main/service/info/config_test.js | 87 +++++++++++++++++++- .../common/configs/configs_loader_test.js | 7 +- 8 files changed, 148 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/dbb35677/ambari-web/app/controllers/main/service/info/configs.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/service/info/configs.js b/ambari-web/app/controllers/main/service/info/configs.js index 319610c..c49bfae 100644 --- a/ambari-web/app/controllers/main/service/info/configs.js +++ b/ambari-web/app/controllers/main/service/info/configs.js @@ -235,6 +235,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.AddSecurityConfi */ clearStep: function () { this.abortRequests(); + App.router.get('mainController').stopPolling(); App.set('componentToBeAdded', {}); App.set('componentToBeDeleted', {}); this.clearLoadInfo(); @@ -288,7 +289,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.AddSecurityConfi acc.push(i); return Array.prototype.concat.apply(acc, App.StackService.find(i).get('dependentServiceNames').toArray()).without(serviceName).uniq(); }, [])); - this.trackRequest(this.loadConfigTheme(serviceName).always(function () { + this.trackRequestChain(this.loadConfigTheme(serviceName).always(function () { if (self.get('preSelectedConfigVersion')) { self.loadPreSelectedConfigVersion(); } else { @@ -299,6 +300,16 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.AddSecurityConfi }, /** + * Turn on polling when all requests are finished + */ + trackRequestsDidChange: function() { + var allCompleted = this.get('requestsInProgress').everyProperty('completed', true); + if (this.get('requestsInProgress').length && allCompleted) { + App.router.get('mainController').startPolling(); + } + }.observes('requestsInProgress.@each.completed'), + + /** * Generate "finger-print" for current <code>stepConfigs[0]</code> * Used to determine, if user has some unsaved changes (comparing with <code>hash</code>) * @returns {string|null} http://git-wip-us.apache.org/repos/asf/ambari/blob/dbb35677/ambari-web/app/mixins/common/configs/configs_loader.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/configs_loader.js b/ambari-web/app/mixins/common/configs/configs_loader.js index 666cef8..403b871 100644 --- a/ambari-web/app/mixins/common/configs/configs_loader.js +++ b/ambari-web/app/mixins/common/configs/configs_loader.js @@ -107,7 +107,7 @@ App.ConfigsLoader = Em.Mixin.create(App.GroupsMappingMixin, { this.set('versionLoaded', false); this.set('selectedVersion', this.get('currentDefaultVersion')); this.set('preSelectedConfigVersion', null); - this.trackRequest(App.ajax.send({ + this.trackRequestChain(App.ajax.send({ name: 'service.serviceConfigVersions.get.current', sender: this, data: { http://git-wip-us.apache.org/repos/asf/ambari/blob/dbb35677/ambari-web/app/mixins/common/track_request_mixin.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/track_request_mixin.js b/ambari-web/app/mixins/common/track_request_mixin.js index 6fcc991..07eaf6e 100644 --- a/ambari-web/app/mixins/common/track_request_mixin.js +++ b/ambari-web/app/mixins/common/track_request_mixin.js @@ -28,7 +28,36 @@ App.TrackRequestMixin = Em.Mixin.create({ * @method trackRequest */ trackRequest: function (request) { - this.get('requestsInProgress').push(request); + var requestId = this.get('requestsInProgress').length; + var self = this; + this.get('requestsInProgress').pushObject({ + request: request, + id: requestId, + status: request.state(), + completed: ['resolved', 'rejected'].contains(request.state()) + }); + request.always(function() { + Em.setProperties(self.get('requestsInProgress').findProperty('id', requestId), { + completed: true, + status: request.state() + }); + }); + }, + + /** + * This method used to put promise to requests queue which is waiting for another request to be put in tracking queue + * after tracking request promise will be completed. Basically it used for places where trackRequest called after + * tracked promise gets resolved. + * + * @param {$.ajax} request + */ + trackRequestChain: function(request) { + var dfd = $.Deferred(); + request.always(function() { + dfd.resolve(); + }); + this.trackRequest(request); + this.trackRequest(dfd); }, /** @@ -37,8 +66,9 @@ App.TrackRequestMixin = Em.Mixin.create({ */ abortRequests: function () { this.get('requestsInProgress').forEach(function(r) { - if (r && r.readyState !== 4) { - r.abort(); + var request = r.request; + if (request && request.readyState !== undefined && request.readyState !== 4) { + request.abort(); } }); this.get('requestsInProgress').clear(); http://git-wip-us.apache.org/repos/asf/ambari/blob/dbb35677/ambari-web/app/routes/main.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/routes/main.js b/ambari-web/app/routes/main.js index 354198b..9ed2dd1 100644 --- a/ambari-web/app/routes/main.js +++ b/ambari-web/app/routes/main.js @@ -229,7 +229,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, { controller.connectOutlet('mainHostSummary'); }); } else - controller.connectOutlet('mainHostSummary'); + controller.connectOutlet('mainHostSummary'); }); } else if(App.Service.find().mapProperty('serviceName').contains('HIVE')) { App.router.get('configurationController').getConfigsByTags(tags).always(function () { @@ -718,6 +718,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, { //if service is not existed then route to default service if (item.get('isLoaded')) { if (router.get('mainServiceItemController.isConfigurable')) { + router.get('mainController').stopPolling(); router.get('mainServiceItemController').connectOutlet('mainServiceInfoConfigs', item); } else { @@ -730,13 +731,19 @@ module.exports = Em.Route.extend(App.RouterRedirections, { } }); }, - exitRoute: function (router, context, callback) { + exitRoute: function (router, nextRoute, callback) { var controller = router.get('mainServiceInfoConfigsController'); + var exitCallback = function() { + if (!/\/main\/services\/\w+\/configs$/.test(nextRoute)) { + router.get('mainController').startPolling(); + } + callback(); + }; // If another user is running some wizard, current user can't save configs if (controller.hasUnsavedChanges() && !router.get('wizardWatcherController.isWizardRunning')) { - controller.showSavePopup(callback); + controller.showSavePopup(exitCallback); } else { - callback(); + exitCallback(); } } }), http://git-wip-us.apache.org/repos/asf/ambari/blob/dbb35677/ambari-web/test/controllers/main/host/configs_service_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/host/configs_service_test.js b/ambari-web/test/controllers/main/host/configs_service_test.js index deb01ae..85f98b3 100644 --- a/ambari-web/test/controllers/main/host/configs_service_test.js +++ b/ambari-web/test/controllers/main/host/configs_service_test.js @@ -107,11 +107,13 @@ describe('App.MainHostServiceConfigsController', function () { sinon.stub(controller, 'loadConfigTheme', function() { return { always: Em.K }; }); + sinon.stub(controller, 'trackRequest'); }); afterEach(function() { controller.loadCurrentVersions.restore(); controller.loadConfigTheme.restore(); App.themesMapper.generateAdvancedTabs.restore(); + controller.trackRequest.restore(); }); it("should set host", function () { controller.set('content', { http://git-wip-us.apache.org/repos/asf/ambari/blob/dbb35677/ambari-web/test/controllers/main/host/details_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/host/details_test.js b/ambari-web/test/controllers/main/host/details_test.js index 7f3fcf7..17b1eec 100644 --- a/ambari-web/test/controllers/main/host/details_test.js +++ b/ambari-web/test/controllers/main/host/details_test.js @@ -38,6 +38,7 @@ describe('App.MainHostDetailsController', function () { beforeEach(function () { controller = getController(); + sinon.stub(controller, 'trackRequest'); }); App.TestAliases.testAsComputedFilterBy(getController(), 'serviceNonClientActiveComponents', 'serviceActiveComponents', 'isClient', false); http://git-wip-us.apache.org/repos/asf/ambari/blob/dbb35677/ambari-web/test/controllers/main/service/info/config_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/service/info/config_test.js b/ambari-web/test/controllers/main/service/info/config_test.js index ce98c0c..8ca48b1 100644 --- a/ambari-web/test/controllers/main/service/info/config_test.js +++ b/ambari-web/test/controllers/main/service/info/config_test.js @@ -38,6 +38,8 @@ describe("App.MainServiceInfoConfigsController", function () { beforeEach(function () { sinon.stub(App.themesMapper, 'generateAdvancedTabs').returns(Em.K); + sinon.stub(App.router.get('mainController'), 'startPolling'); + sinon.stub(App.router.get('mainController'), 'stopPolling'); mainServiceInfoConfigsController = getController(); }); @@ -45,6 +47,8 @@ describe("App.MainServiceInfoConfigsController", function () { afterEach(function() { App.themesMapper.generateAdvancedTabs.restore(); + App.router.get('mainController').startPolling.restore(); + App.router.get('mainController').stopPolling.restore(); }); describe("#getHash", function () { @@ -256,12 +260,14 @@ describe("App.MainServiceInfoConfigsController", function () { sinon.stub(mainServiceInfoConfigsController, "getHash", function () { return "hash" }); + sinon.stub(mainServiceInfoConfigsController, 'trackRequest'); }); afterEach(function () { mainServiceInfoConfigsController.get.restore(); mainServiceInfoConfigsController.restartServicePopup.restore(); mainServiceInfoConfigsController.getHash.restore(); + mainServiceInfoConfigsController.trackRequest.restore(); }); tests.forEach(function (t) { @@ -822,9 +828,86 @@ describe("App.MainServiceInfoConfigsController", function () { mainServiceInfoConfigsController.get('requestsInProgress').clear(); }); it("should set requestsInProgress", function () { + var dfd = $.Deferred(); mainServiceInfoConfigsController.get('requestsInProgress').clear(); - mainServiceInfoConfigsController.trackRequest({'request': {}}); - expect(mainServiceInfoConfigsController.get('requestsInProgress')[0]).to.eql({'request': {}}); + mainServiceInfoConfigsController.trackRequest(dfd); + expect(mainServiceInfoConfigsController.get('requestsInProgress')[0]).to.eql( + { + request: dfd, + id: 0, + status: 'pending', + completed: false + } + ); + }); + it('should update request status when it become resolved', function() { + var request = $.Deferred(); + mainServiceInfoConfigsController.get('requestsInProgress').clear(); + mainServiceInfoConfigsController.trackRequest(request); + expect(mainServiceInfoConfigsController.get('requestsInProgress')[0]).to.eql({ + request: request, + id: 0, + status: 'pending', + completed: false + }); + request.resolve(); + expect(mainServiceInfoConfigsController.get('requestsInProgress')[0]).to.eql({ + request: request, + id: 0, + status: 'resolved', + completed: true + }); + }); + }); + + describe('#trackRequestChain', function() { + beforeEach(function() { + mainServiceInfoConfigsController.get('requestsInProgress').clear(); + }); + it('should set 2 requests in to requestsInProgress list', function() { + mainServiceInfoConfigsController.trackRequestChain($.Deferred()); + expect(mainServiceInfoConfigsController.get('requestsInProgress')).to.have.length(2); + }); + it('should update status for both requests when tracked requests become resolved', function() { + var request = $.Deferred(), + requests; + mainServiceInfoConfigsController.trackRequestChain(request); + requests = mainServiceInfoConfigsController.get('requestsInProgress'); + assert.deepEqual(requests.mapProperty('status'), ['pending', 'pending'], 'initial statuses'); + assert.deepEqual(requests.mapProperty('completed'), [false, false], 'initial completed'); + request.reject(); + assert.deepEqual(requests.mapProperty('status'), ['rejected', 'resolved'], 'update status when rejected'); + assert.deepEqual(requests.mapProperty('completed'), [true, true], 'initial complete are false'); + }); + }); + + describe('#abortRequests', function() { + beforeEach(function() { + mainServiceInfoConfigsController.get('requestsInProgress').clear(); + }); + it('should clear requests when abort called', function() { + mainServiceInfoConfigsController.trackRequest($.Deferred()); + mainServiceInfoConfigsController.abortRequests(); + expect(mainServiceInfoConfigsController.get('requestsInProgress')).to.have.length(0); + }); + it('should abort requests which are not finished', function() { + var pendingRequest = { + abort: sinon.spy(), + readyState: 0, + state: sinon.spy(), + always: sinon.spy() + }; + var finishedRequest = { + abort: sinon.spy(), + readyState: 4, + state: sinon.spy(), + always: sinon.spy() + }; + mainServiceInfoConfigsController.trackRequest(pendingRequest); + mainServiceInfoConfigsController.trackRequest(finishedRequest); + mainServiceInfoConfigsController.abortRequests(); + expect(pendingRequest.abort.calledOnce).to.be.true; + expect(finishedRequest.abort.calledOnce).to.be.false; }); }); http://git-wip-us.apache.org/repos/asf/ambari/blob/dbb35677/ambari-web/test/mixins/common/configs/configs_loader_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/mixins/common/configs/configs_loader_test.js b/ambari-web/test/mixins/common/configs/configs_loader_test.js index 8f22b44..749e5d3 100644 --- a/ambari-web/test/mixins/common/configs/configs_loader_test.js +++ b/ambari-web/test/mixins/common/configs/configs_loader_test.js @@ -223,14 +223,14 @@ describe('App.ConfigsLoader', function() { describe("#loadCurrentVersions()", function () { beforeEach(function() { - sinon.stub(mixin, 'trackRequest'); + sinon.stub(mixin, 'trackRequestChain'); mixin.set('currentDefaultVersion', {}); mixin.set('servicesToLoad', ['S1', 'S2']); mixin.loadCurrentVersions(); }); afterEach(function() { - mixin.trackRequest.restore(); + mixin.trackRequestChain.restore(); }); it("isCompareMode should be false", function() { @@ -250,7 +250,7 @@ describe('App.ConfigsLoader', function() { }); it("trackRequest should be called", function() { - expect(mixin.trackRequest.calledOnce).to.be.true; + expect(mixin.trackRequestChain.calledOnce).to.be.true; }); it("App.ajax.send should be called", function() { @@ -490,4 +490,3 @@ describe('App.ConfigsLoader', function() { }); }); -