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, return
Error: err.Error(), }
})
return results := make([]*api.Repository, len(repos))
} for i, repo := range repos {
accessMode, err := models.AccessLevel(ctx.Doer, repo) results[i] = &api.Repository{
if err != nil { ID: repo.ID,
ctx.JSON(http.StatusInternalServerError, api.SearchError{ FullName: repo.FullName(),
OK: false, Fork: repo.IsFork,
Error: err.Error(), Private: repo.IsPrivate,
}) Template: repo.IsTemplate,
} Mirror: repo.IsMirror,
results[i] = convert.ToRepo(repo, accessMode) Stars: repo.NumStars,
HTMLURL: repo.HTMLURL(),
Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate,
}
} }
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;
if (searchedURL === this.searchURL) { try {
this.repos = result.data; if (!this.reposTotalCount) {
const count = request.getResponseHeader('X-Total-Count'); const totalCountSearchURL = `${this.subUrl}/repo/search?count_only=1&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`;
if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') { response = await fetch(totalCountSearchURL);
this.reposTotalCount = count; this.reposTotalCount = response.headers.get('X-Total-Count');
}
Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, count);
this.finalPage = Math.ceil(count / this.searchLimit);
this.updateHistory();
} }
}).always(() => {
response = await fetch(searchedURL);
json = await response.json();
} catch {
if (searchedURL === this.searchURL) { if (searchedURL === this.searchURL) {
this.isLoading = false; this.isLoading = false;
} }
}); return;
}
if (searchedURL === this.searchURL) {
this.repos = json.data;
const count = response.headers.get('X-Total-Count');
if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') {
this.reposTotalCount = count;
}
Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, count);
this.finalPage = Math.ceil(count / this.searchLimit);
this.updateHistory();
this.isLoading = false;
}
}, },
repoIcon(repo) { repoIcon(repo) {