Browse Source

release: improve page load performance

Previously, we load all releases of a repository which could hurt
performance when the repository has a lot of releases.

Now we're able to only load releases in current page view we need
to show by matching with 'tag_name'.
pull/4280/head
Unknwon 8 years ago
parent
commit
451aef7a1c
No known key found for this signature in database
GPG Key ID: 25B575AE3213B2B3
  1. 2
      gogs.go
  2. 99
      models/release.go
  3. 78
      routers/repo/release.go
  4. 2
      templates/.VERSION
  5. 2
      vendor/github.com/gogits/git-module/git.go
  6. 2
      vendor/github.com/gogits/git-module/repo_tag.go
  7. 6
      vendor/vendor.json

2
gogs.go

@ -16,7 +16,7 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const APP_VER = "0.10.12.0309" const APP_VER = "0.10.12.0310"
func init() { func init() {
setting.AppVer = APP_VER setting.AppVer = APP_VER

99
models/release.go

@ -51,6 +51,26 @@ func (r *Release) AfterSet(colName string, _ xorm.Cell) {
} }
} }
func (r *Release) loadAttributes(e Engine) (err error) {
if r.Publisher == nil {
r.Publisher, err = getUserByID(e, r.PublisherID)
if err != nil {
if IsErrUserNotExist(err) {
r.PublisherID = -1
r.Publisher = NewGhostUser()
} else {
return fmt.Errorf("getUserByID.(Publisher) [%d]: %v", r.PublisherID, err)
}
}
}
return nil
}
func (r *Release) LoadAttributes() error {
return r.loadAttributes(x)
}
// IsReleaseExist returns true if release with given tag name already exists. // IsReleaseExist returns true if release with given tag name already exists.
func IsReleaseExist(repoID int64, tagName string) (bool, error) { func IsReleaseExist(repoID int64, tagName string) (bool, error) {
if len(tagName) == 0 { if len(tagName) == 0 {
@ -60,31 +80,31 @@ func IsReleaseExist(repoID int64, tagName string) (bool, error) {
return x.Get(&Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)}) return x.Get(&Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)})
} }
func createTag(gitRepo *git.Repository, rel *Release) error { func createTag(gitRepo *git.Repository, r *Release) error {
// Only actual create when publish. // Only actual create when publish.
if !rel.IsDraft { if !r.IsDraft {
if !gitRepo.IsTagExist(rel.TagName) { if !gitRepo.IsTagExist(r.TagName) {
commit, err := gitRepo.GetBranchCommit(rel.Target) commit, err := gitRepo.GetBranchCommit(r.Target)
if err != nil { if err != nil {
return fmt.Errorf("GetBranchCommit: %v", err) return fmt.Errorf("GetBranchCommit: %v", err)
} }
// Trim '--' prefix to prevent command line argument vulnerability. // Trim '--' prefix to prevent command line argument vulnerability.
rel.TagName = strings.TrimPrefix(rel.TagName, "--") r.TagName = strings.TrimPrefix(r.TagName, "--")
if err = gitRepo.CreateTag(rel.TagName, commit.ID.String()); err != nil { if err = gitRepo.CreateTag(r.TagName, commit.ID.String()); err != nil {
if strings.Contains(err.Error(), "is not a valid tag name") { if strings.Contains(err.Error(), "is not a valid tag name") {
return ErrInvalidTagName{rel.TagName} return ErrInvalidTagName{r.TagName}
} }
return err return err
} }
} else { } else {
commit, err := gitRepo.GetTagCommit(rel.TagName) commit, err := gitRepo.GetTagCommit(r.TagName)
if err != nil { if err != nil {
return fmt.Errorf("GetTagCommit: %v", err) return fmt.Errorf("GetTagCommit: %v", err)
} }
rel.Sha1 = commit.ID.String() r.Sha1 = commit.ID.String()
rel.NumCommits, err = commit.CommitsCount() r.NumCommits, err = commit.CommitsCount()
if err != nil { if err != nil {
return fmt.Errorf("CommitsCount: %v", err) return fmt.Errorf("CommitsCount: %v", err)
} }
@ -94,19 +114,19 @@ func createTag(gitRepo *git.Repository, rel *Release) error {
} }
// CreateRelease creates a new release of repository. // CreateRelease creates a new release of repository.
func CreateRelease(gitRepo *git.Repository, rel *Release) error { func CreateRelease(gitRepo *git.Repository, r *Release) error {
isExist, err := IsReleaseExist(rel.RepoID, rel.TagName) isExist, err := IsReleaseExist(r.RepoID, r.TagName)
if err != nil { if err != nil {
return err return err
} else if isExist { } else if isExist {
return ErrReleaseAlreadyExist{rel.TagName} return ErrReleaseAlreadyExist{r.TagName}
} }
if err = createTag(gitRepo, rel); err != nil { if err = createTag(gitRepo, r); err != nil {
return err return err
} }
rel.LowerTagName = strings.ToLower(rel.TagName) r.LowerTagName = strings.ToLower(r.TagName)
_, err = x.InsertOne(rel) _, err = x.InsertOne(r)
return err return err
} }
@ -119,53 +139,68 @@ func GetRelease(repoID int64, tagName string) (*Release, error) {
return nil, ErrReleaseNotExist{0, tagName} return nil, ErrReleaseNotExist{0, tagName}
} }
rel := &Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)} r := &Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)}
_, err = x.Get(rel) if _, err = x.Get(r); err != nil {
return rel, err return nil, fmt.Errorf("Get: %v", err)
}
return r, r.LoadAttributes()
} }
// GetReleaseByID returns release with given ID. // GetReleaseByID returns release with given ID.
func GetReleaseByID(id int64) (*Release, error) { func GetReleaseByID(id int64) (*Release, error) {
rel := new(Release) r := new(Release)
has, err := x.Id(id).Get(rel) has, err := x.Id(id).Get(r)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !has {
return nil, ErrReleaseNotExist{id, ""} return nil, ErrReleaseNotExist{id, ""}
} }
return rel, nil return r, r.LoadAttributes()
}
// GetPublishedReleasesByRepoID returns a list of published releases of repository.
// If matches is not empty, only published releases in matches will be returned.
// In any case, drafts won't be returned by this function.
func GetPublishedReleasesByRepoID(repoID int64, matches ...string) ([]*Release, error) {
sess := x.Where("repo_id = ?", repoID).And("is_draft = ?", false).Desc("created_unix")
if len(matches) > 0 {
sess.In("tag_name", matches)
}
releases := make([]*Release, 0, 5)
return releases, sess.Find(&releases, new(Release))
} }
// GetReleasesByRepoID returns a list of releases of repository. // GetDraftReleasesByRepoID returns all draft releases of repository.
func GetReleasesByRepoID(repoID int64) (rels []*Release, err error) { func GetDraftReleasesByRepoID(repoID int64) ([]*Release, error) {
err = x.Desc("created_unix").Find(&rels, Release{RepoID: repoID}) releases := make([]*Release, 0)
return rels, err return releases, x.Where("repo_id = ?", repoID).And("is_draft = ?", true).Find(&releases)
} }
type ReleaseSorter struct { type ReleaseSorter struct {
rels []*Release releases []*Release
} }
func (rs *ReleaseSorter) Len() int { func (rs *ReleaseSorter) Len() int {
return len(rs.rels) return len(rs.releases)
} }
func (rs *ReleaseSorter) Less(i, j int) bool { func (rs *ReleaseSorter) Less(i, j int) bool {
diffNum := rs.rels[i].NumCommits - rs.rels[j].NumCommits diffNum := rs.releases[i].NumCommits - rs.releases[j].NumCommits
if diffNum != 0 { if diffNum != 0 {
return diffNum > 0 return diffNum > 0
} }
return rs.rels[i].Created.After(rs.rels[j].Created) return rs.releases[i].Created.After(rs.releases[j].Created)
} }
func (rs *ReleaseSorter) Swap(i, j int) { func (rs *ReleaseSorter) Swap(i, j int) {
rs.rels[i], rs.rels[j] = rs.rels[j], rs.rels[i] rs.releases[i], rs.releases[j] = rs.releases[j], rs.releases[i]
} }
// SortReleases sorts releases by number of commits and created time. // SortReleases sorts releases by number of commits and created time.
func SortReleases(rels []*Release) { func SortReleases(rels []*Release) {
sorter := &ReleaseSorter{rels: rels} sorter := &ReleaseSorter{releases: rels}
sort.Sort(sorter) sort.Sort(sorter)
} }

78
routers/repo/release.go

@ -59,35 +59,26 @@ func Releases(ctx *context.Context) {
return return
} }
// FIXME: should only get releases match tags result and drafts. releases, err := models.GetPublishedReleasesByRepoID(ctx.Repo.Repository.ID, tagsResult.Tags...)
releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID)
if err != nil { if err != nil {
ctx.Handle(500, "GetReleasesByRepoID", err) ctx.Handle(500, "GetPublishedReleasesByRepoID", err)
return return
} }
// Temproray cache commits count of used branches to speed up. // Temproray cache commits count of used branches to speed up.
countCache := make(map[string]int64) countCache := make(map[string]int64)
drafts := make([]*models.Release, 0, 1) results := make([]*models.Release, len(tagsResult.Tags))
tags := make([]*models.Release, len(tagsResult.Tags))
for i, rawTag := range tagsResult.Tags { for i, rawTag := range tagsResult.Tags {
for j, r := range releases { for j, r := range releases {
if r == nil || if r == nil || r.TagName != rawTag {
(r.IsDraft && !ctx.Repo.IsOwner()) ||
(!r.IsDraft && r.TagName != rawTag) {
continue continue
} }
releases[j] = nil // Mark as used. releases[j] = nil // Mark as used.
r.Publisher, err = models.GetUserByID(r.PublisherID) if err = r.LoadAttributes(); err != nil {
if err != nil { ctx.Handle(500, "LoadAttributes", err)
if models.IsErrUserNotExist(err) { return
r.Publisher = models.NewGhostUser()
} else {
ctx.Handle(500, "GetUserByID", err)
return
}
} }
if err := calReleaseNumCommitsBehind(ctx.Repo, r, countCache); err != nil { if err := calReleaseNumCommitsBehind(ctx.Repo, r, countCache); err != nil {
@ -96,49 +87,68 @@ func Releases(ctx *context.Context) {
} }
r.Note = markdown.RenderString(r.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()) r.Note = markdown.RenderString(r.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())
if r.TagName == rawTag { results[i] = r
tags[i] = r break
break
}
if r.IsDraft {
drafts = append(drafts, r)
}
} }
if tags[i] == nil { // No published release matches this tag
if results[i] == nil {
commit, err := ctx.Repo.GitRepo.GetTagCommit(rawTag) commit, err := ctx.Repo.GitRepo.GetTagCommit(rawTag)
if err != nil { if err != nil {
ctx.Handle(500, "GetTagCommit", err) ctx.Handle(500, "GetTagCommit", err)
return return
} }
tags[i] = &models.Release{ results[i] = &models.Release{
Title: rawTag, Title: rawTag,
TagName: rawTag, TagName: rawTag,
Sha1: commit.ID.String(), Sha1: commit.ID.String(),
} }
tags[i].NumCommits, err = commit.CommitsCount() results[i].NumCommits, err = commit.CommitsCount()
if err != nil { if err != nil {
ctx.Handle(500, "CommitsCount", err) ctx.Handle(500, "CommitsCount", err)
return return
} }
tags[i].NumCommitsBehind = ctx.Repo.CommitsCount - tags[i].NumCommits results[i].NumCommitsBehind = ctx.Repo.CommitsCount - results[i].NumCommits
} }
} }
models.SortReleases(results)
// Only show drafts if user is viewing the latest page
var drafts []*models.Release
if tagsResult.HasLatest {
drafts, err = models.GetDraftReleasesByRepoID(ctx.Repo.Repository.ID)
if err != nil {
ctx.Handle(500, "GetDraftReleasesByRepoID", err)
return
}
for _, r := range drafts {
if err = r.LoadAttributes(); err != nil {
ctx.Handle(500, "LoadAttributes", err)
return
}
models.SortReleases(tags) if err := calReleaseNumCommitsBehind(ctx.Repo, r, countCache); err != nil {
if len(drafts) > 0 && tagsResult.HasLatest { ctx.Handle(500, "calReleaseNumCommitsBehind", err)
tags = append(drafts, tags...) return
}
r.Note = markdown.RenderString(r.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())
}
if len(drafts) > 0 {
results = append(drafts, results...)
}
} }
ctx.Data["Releases"] = tags ctx.Data["Releases"] = results
ctx.Data["HasPrevious"] = !tagsResult.HasLatest ctx.Data["HasPrevious"] = !tagsResult.HasLatest
ctx.Data["ReachEnd"] = tagsResult.ReachEnd ctx.Data["ReachEnd"] = tagsResult.ReachEnd
ctx.Data["PreviousAfter"] = tagsResult.PreviousAfter ctx.Data["PreviousAfter"] = tagsResult.PreviousAfter
if len(tags) > 0 { if len(results) > 0 {
ctx.Data["NextAfter"] = tags[len(tags)-1].TagName ctx.Data["NextAfter"] = results[len(results)-1].TagName
} }
ctx.HTML(200, RELEASES) ctx.HTML(200, RELEASES)
} }

2
templates/.VERSION

@ -1 +1 @@
0.10.12.0309 0.10.12.0310

2
vendor/github.com/gogits/git-module/git.go generated vendored

@ -10,7 +10,7 @@ import (
"time" "time"
) )
const _VERSION = "0.4.12" const _VERSION = "0.4.13"
func Version() string { func Version() string {
return _VERSION return _VERSION

2
vendor/github.com/gogits/git-module/repo_tag.go generated vendored

@ -171,7 +171,7 @@ func (repo *Repository) GetTagsAfter(after string, limit int) (*TagsResult, erro
} }
if allTags[i] == after { if allTags[i] == after {
hasMatch = true hasMatch = true
if limit > 0 && i-limit > 0 { if limit > 0 && i-limit >= 0 {
previousAfter = allTags[i-limit] previousAfter = allTags[i-limit]
} }
continue continue

6
vendor/vendor.json vendored

@ -159,10 +159,10 @@
"revisionTime": "2016-08-10T03:50:02Z" "revisionTime": "2016-08-10T03:50:02Z"
}, },
{ {
"checksumSHA1": "RnWB5UDcTpSGepvWnwic1SyRZrc=", "checksumSHA1": "sygMoTVpNmOTbsnZbgLR904p5GE=",
"path": "github.com/gogits/git-module", "path": "github.com/gogits/git-module",
"revision": "1b9552bab7499e5cbd96f6b550aaa0038faf6b67", "revision": "2c083b82191752340c76271876671e18130ad662",
"revisionTime": "2017-02-19T18:16:29Z" "revisionTime": "2017-03-10T19:06:55Z"
}, },
{ {
"checksumSHA1": "Rvj0LCHGhFQyIM7MzBPt1iRP89c=", "checksumSHA1": "Rvj0LCHGhFQyIM7MzBPt1iRP89c=",

Loading…
Cancel
Save