email: rewrite loading of email status using async

we start a bunch of requests in the background for each domain. when
we switch views immediately, to say the eventlog, these requests are
still active in the background.

canceling the requests will require a much bigger refactor.

https://forum.cloudron.io/topic/10434/email-event-log-loading-very-slowly-seems-tied-to-overall-email-domain-list-health-checks
This commit is contained in:
Girish Ramakrishnan
2024-01-09 14:59:29 +01:00
parent ca31dc8d78
commit 7a5e990ad4
3 changed files with 84 additions and 31 deletions

View File

@@ -1,6 +1,7 @@
'use strict';
/* global $, angular, TASK_TYPES */
/* global async */
angular.module('Application').controller('EmailsController', ['$scope', '$location', '$translate', '$timeout', 'Client', function ($scope, $location, $translate, $timeout, Client) {
Client.onReady(function () { if (!Client.getUserInfo().isAtLeastMailManager) $location.path('/'); });
@@ -404,44 +405,83 @@ angular.module('Application').controller('EmailsController', ['$scope', '$locati
}
};
function refreshDomainStatuses() {
$scope.domains.forEach(function (domain) {
domain.usage = null; // used by ui to show 'loading'
function refreshMailStatus(domain, done) {
Client.getMailStatusForDomain(domain.domain, function (error, result) {
if (error) {
console.error('Failed to fetch mail status for domain', domain.domain, error);
return done();
}
Client.getMailStatusForDomain(domain.domain, function (error, result) {
if (error) return console.error('Failed to fetch mail status for domain', domain.domain, error);
domain.status = result;
domain.status = result;
domain.statusOk = Object.keys(result).every(function (k) {
if (k === 'dns') return Object.keys(result.dns).every(function (k) { return result.dns[k].status; });
domain.statusOk = Object.keys(result).every(function (k) {
if (k === 'dns') return Object.keys(result.dns).every(function (k) { return result.dns[k].status; });
if (!('status' in result[k])) return true; // if status is not present, the test was not run
if (!('status' in result[k])) return true; // if status is not present, the test was not run
return result[k].status;
});
return result[k].status;
});
Client.getMailConfigForDomain(domain.domain, function (error, mailConfig) {
if (error) return console.error('Failed to fetch mail config for domain', domain.domain, error);
done();
});
}
domain.inbound = mailConfig.enabled;
domain.outbound = mailConfig.relay.provider !== 'noop';
function refreshMailConfig(domain, done) {
Client.getMailConfigForDomain(domain.domain, function (error, mailConfig) {
if (error) {
console.error('Failed to fetch mail config for domain', domain.domain, error);
return done();
}
// do this even if no outbound since people forget to remove mailboxes
Client.getMailboxCount(domain.domain, function (error, count) {
if (error) return console.error('Failed to fetch mailboxes for domain', domain.domain, error);
domain.inbound = mailConfig.enabled;
domain.outbound = mailConfig.relay.provider !== 'noop';
domain.mailboxCount = count;
// do this even if no outbound since people forget to remove mailboxes
Client.getMailboxCount(domain.domain, function (error, count) {
if (error) {
console.error('Failed to fetch mailboxes for domain', domain.domain, error);
return done();
}
Client.getMailUsage(domain.domain, function (error, usage) {
if (error) return console.error('Failed to fetch usage for domain', domain.domain, error);
domain.mailboxCount = count;
domain.usage = 0;
// we used to use quotaValue here but it's quite different wrt diskSize. so choose diskSize consistently
// also, quotaValue can be missing for deleted mailboxes that are on disk but removed from dovecot/ldap itself
Object.keys(usage).forEach(function (m) { domain.usage += usage[m].diskSize; });
});
done();
});
});
}
function refreshMailUsage(domain, done) {
Client.getMailUsage(domain.domain, function (error, usage) {
if (error) {
console.error('Failed to fetch usage for domain', domain.domain, error);
return done();
}
domain.usage = 0;
// we used to use quotaValue here but it's quite different wrt diskSize. so choose diskSize consistently
// also, quotaValue can be missing for deleted mailboxes that are on disk but removed from dovecot/ldap itself
Object.keys(usage).forEach(function (m) { domain.usage += usage[m].diskSize; });
done();
});
}
function refreshDomainStatuses() {
async.each($scope.domains, function (domain, iteratorDone) {
async.series([
refreshMailStatus.bind(null, domain),
refreshMailConfig.bind(null, domain),
], function () {
domain.loading = false;
iteratorDone();
});
}, function () {
// mail usage is loaded separately with a cancellation check. when there are a lot of domains, it runs a long time in background and slows down loading of new views
async.eachLimit($scope.domains, 5, function (domain, itemDone) {
if ($scope.$$destroyed) return itemDone(); // abort!
refreshMailUsage(domain, function () {
domain.loadingUsage = false;
itemDone();
});
});
});
@@ -451,7 +491,9 @@ angular.module('Application').controller('EmailsController', ['$scope', '$locati
Client.getDomains(function (error, domains) {
if (error) return console.error('Unable to get domain listing.', error);
domains.forEach(function (domain) { domain.loading = true; domain.loadingUsage = true; }); // used by ui to show 'loading'
$scope.domains = domains;
$scope.ready = true;
if ($scope.user.isAtLeastAdmin) {