Improve dashboard's repo list performance (#18963)

* Improve dashboard's repo list performance

- Avoid a lot of database lookups for all the repo's, by adding a
undocumented "minimal" mode for this specific task, which returns the
data that's only needed by this list which doesn't require any database
lookups.
- Makes fetching these list faster.
- Less CPU overhead when a user visits home page.

* Refactor javascript code + fix Fork icon

- Use async in the function so we can use `await`.
- Remove `archivedFilter` check for count, as it doesn't make sense to
  show the count of repos when you can't even see them(as they are
  filited away).

* Add `count_only`

* Remove uncessary code

* Improve comment

Co-authored-by: delvh <dev.lh@web.de>

* Update web_src/js/components/DashboardRepoList.js

Co-authored-by: delvh <dev.lh@web.de>

* Update web_src/js/components/DashboardRepoList.js

Co-authored-by: delvh <dev.lh@web.de>

* By default apply minimal mode

* Remove `minimal` paramater

* Refactor count header

* Simplify init

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
Gusted 2022-04-26 20:34:30 +00:00 committed by GitHub
parent 89eec15dd9
commit 076eaad743
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 39 deletions

View file

@ -218,7 +218,6 @@ func Search(ctx *context.APIContext) {
} }
results[i] = convert.ToRepo(repo, accessMode) results[i] = convert.ToRepo(repo, accessMode)
} }
ctx.SetLinkHeader(int(count), opts.PageSize) ctx.SetLinkHeader(int(count), opts.PageSize)
ctx.SetTotalCountHeader(count) ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, api.SearchResults{ ctx.JSON(http.StatusOK, api.SearchResults{

View file

@ -590,26 +590,28 @@ func SearchRepo(ctx *context.Context) {
return return
} }
results := make([]*api.Repository, len(repos)) ctx.SetTotalCountHeader(count)
for i, repo := range repos {
if err = repo.GetOwner(ctx); err != nil { // To improve performance when only the count is requested
ctx.JSON(http.StatusInternalServerError, api.SearchError{ if ctx.FormBool("count_only") {
OK: false,
Error: err.Error(),
})
return return
} }
accessMode, err := models.AccessLevel(ctx.Doer, repo)
if err != nil { results := make([]*api.Repository, len(repos))
ctx.JSON(http.StatusInternalServerError, api.SearchError{ for i, repo := range repos {
OK: false, results[i] = &api.Repository{
Error: err.Error(), ID: repo.ID,
}) FullName: repo.FullName(),
Fork: repo.IsFork,
Private: repo.IsPrivate,
Template: repo.IsTemplate,
Mirror: repo.IsMirror,
Stars: repo.NumStars,
HTMLURL: repo.HTMLURL(),
Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate,
} }
results[i] = convert.ToRepo(repo, accessMode)
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, api.SearchResults{ ctx.JSON(http.StatusOK, api.SearchResults{
OK: true, OK: true,
Data: results, Data: results,

View file

@ -298,36 +298,41 @@ function initVueComponents() {
this.searchRepos(); this.searchRepos();
}, },
searchRepos() { async searchRepos() {
this.isLoading = true; this.isLoading = true;
if (!this.reposTotalCount) {
const totalCountSearchURL = `${this.subUrl}/repo/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`;
$.getJSON(totalCountSearchURL, (_result, _textStatus, request) => {
this.reposTotalCount = request.getResponseHeader('X-Total-Count');
});
}
const searchedMode = this.repoTypes[this.reposFilter].searchMode; const searchedMode = this.repoTypes[this.reposFilter].searchMode;
const searchedURL = this.searchURL; const searchedURL = this.searchURL;
const searchedQuery = this.searchQuery; const searchedQuery = this.searchQuery;
$.getJSON(searchedURL, (result, _textStatus, request) => { let response, json;
try {
if (!this.reposTotalCount) {
const totalCountSearchURL = `${this.subUrl}/repo/search?count_only=1&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`;
response = await fetch(totalCountSearchURL);
this.reposTotalCount = response.headers.get('X-Total-Count');
}
response = await fetch(searchedURL);
json = await response.json();
} catch {
if (searchedURL === this.searchURL) { if (searchedURL === this.searchURL) {
this.repos = result.data; this.isLoading = false;
const count = request.getResponseHeader('X-Total-Count'); }
return;
}
if (searchedURL === this.searchURL) {
this.repos = json.data;
const count = response.headers.get('X-Total-Count');
if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') { if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') {
this.reposTotalCount = count; this.reposTotalCount = count;
} }
Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, count); Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, count);
this.finalPage = Math.ceil(count / this.searchLimit); this.finalPage = Math.ceil(count / this.searchLimit);
this.updateHistory(); this.updateHistory();
}
}).always(() => {
if (searchedURL === this.searchURL) {
this.isLoading = false; this.isLoading = false;
} }
});
}, },
repoIcon(repo) { repoIcon(repo) {