Skip to content

Commit 2e6e309

Browse files
committed
Some improvements
1 parent d6e94fa commit 2e6e309

File tree

2 files changed

+51
-48
lines changed

2 files changed

+51
-48
lines changed

routers/web/repo/contributors.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package repo
55

66
import (
7+
std_ctx "context"
78
"errors"
89
"net/http"
910

@@ -27,7 +28,7 @@ func Contributors(ctx *context.Context) {
2728
// ContributorsData renders JSON of contributors along with their weekly commit statistics
2829
func ContributorsData(ctx *context.Context) {
2930
if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil {
30-
if errors.Is(err, contributors_service.ErrAwaitGeneration) {
31+
if errors.Is(err, contributors_service.ErrAwaitGeneration) || errors.Is(err, std_ctx.DeadlineExceeded) {
3132
ctx.Status(http.StatusAccepted)
3233
return
3334
}

services/repository/contributors_graph.go

Lines changed: 49 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"os"
1212
"strconv"
1313
"strings"
14-
"sync"
1514
"time"
1615

1716
"code.gitea.io/gitea/models/avatars"
@@ -20,7 +19,7 @@ import (
2019
"code.gitea.io/gitea/modules/cache"
2120
"code.gitea.io/gitea/modules/git"
2221
"code.gitea.io/gitea/modules/gitrepo"
23-
"code.gitea.io/gitea/modules/graceful"
22+
"code.gitea.io/gitea/modules/globallock"
2423
"code.gitea.io/gitea/modules/log"
2524
api "code.gitea.io/gitea/modules/structs"
2625
)
@@ -32,8 +31,7 @@ const (
3231

3332
var (
3433
ErrAwaitGeneration = errors.New("generation took longer than ")
35-
awaitGenerationTime = time.Second * 5
36-
generateLock = sync.Map{}
34+
awaitGenerationTime = time.Second * 60
3735
)
3836

3937
type WeekData struct {
@@ -81,41 +79,44 @@ func findLastSundayBeforeDate(dateStr string) (string, error) {
8179
func GetContributorStats(ctx context.Context, cache cache.StringCache, repo *repo_model.Repository, revision string) (map[string]*ContributorData, error) {
8280
// as GetContributorStats is resource intensive we cache the result
8381
cacheKey := fmt.Sprintf(contributorStatsCacheKey, repo.FullName(), revision)
84-
if !cache.IsExist(cacheKey) {
85-
genReady := make(chan struct{})
86-
87-
// dont start multiple async generations
88-
_, run := generateLock.Load(cacheKey)
89-
if run {
90-
return nil, ErrAwaitGeneration
82+
if cache.IsExist(cacheKey) {
83+
// TODO: renew timeout of cache cache.UpdateTimeout(cacheKey, contributorStatsCacheTimeout)
84+
var res map[string]*ContributorData
85+
if _, cacheErr := cache.GetJSON(cacheKey, &res); cacheErr != nil {
86+
return nil, fmt.Errorf("cached error: %w", cacheErr.ToError())
9187
}
88+
return res, nil
89+
}
90+
91+
// dont start multiple async generations
92+
releaser, err := globallock.Lock(ctx, cacheKey)
93+
if err != nil {
94+
return nil, err
95+
}
96+
defer releaser()
9297

93-
generateLock.Store(cacheKey, struct{}{})
94-
// run generation async
95-
go generateContributorStats(genReady, cache, cacheKey, repo, revision)
98+
// set a timeout for the generation
99+
ctx, cancel := context.WithTimeout(ctx, awaitGenerationTime)
100+
defer cancel()
96101

97-
select {
98-
case <-time.After(awaitGenerationTime):
99-
return nil, ErrAwaitGeneration
100-
case <-genReady:
101-
// we got generation ready before timeout
102-
break
102+
// run generation async
103+
res, err := generateContributorStats(ctx, cache, cacheKey, repo, revision)
104+
if err != nil {
105+
switch {
106+
case errors.Is(err, context.DeadlineExceeded):
107+
_ = cache.PutJSON(cacheKey, fmt.Errorf("generateContributorStats: %w", ErrAwaitGeneration), contributorStatsCacheTimeout)
108+
default:
109+
_ = cache.PutJSON(cacheKey, fmt.Errorf("generateContributorStats: %w", err), contributorStatsCacheTimeout)
103110
}
111+
return nil, err
104112
}
105-
// TODO: renew timeout of cache cache.UpdateTimeout(cacheKey, contributorStatsCacheTimeout)
106-
var res map[string]*ContributorData
107-
if _, cacheErr := cache.GetJSON(cacheKey, &res); cacheErr != nil {
108-
return nil, fmt.Errorf("cached error: %w", cacheErr.ToError())
109-
}
113+
114+
_ = cache.PutJSON(cacheKey, res, contributorStatsCacheTimeout)
110115
return res, nil
111116
}
112117

113118
// getExtendedCommitStats return the list of *ExtendedCommitStats for the given revision
114-
func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int */) ([]*ExtendedCommitStats, error) {
115-
baseCommit, err := repo.GetCommit(revision)
116-
if err != nil {
117-
return nil, err
118-
}
119+
func getExtendedCommitStats(ctx context.Context, repoPath string, baseCommit *git.Commit, revision string /*, limit int */) ([]*ExtendedCommitStats, error) {
119120
stdoutReader, stdoutWriter, err := os.Pipe()
120121
if err != nil {
121122
return nil, err
@@ -131,15 +132,21 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int
131132

132133
var extendedCommitStats []*ExtendedCommitStats
133134
stderr := new(strings.Builder)
134-
err = gitCmd.Run(repo.Ctx, &git.RunOpts{
135-
Dir: repo.Path,
135+
err = gitCmd.Run(ctx, &git.RunOpts{
136+
Dir: repoPath,
136137
Stdout: stdoutWriter,
137138
Stderr: stderr,
138139
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
139140
_ = stdoutWriter.Close()
140141
scanner := bufio.NewScanner(stdoutReader)
141142

142143
for scanner.Scan() {
144+
select {
145+
case <-ctx.Done():
146+
return ctx.Err()
147+
default:
148+
}
149+
143150
line := strings.TrimSpace(scanner.Text())
144151
if line != "---" {
145152
continue
@@ -199,27 +206,26 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int
199206
return extendedCommitStats, nil
200207
}
201208

202-
func generateContributorStats(genDone chan struct{}, cache cache.StringCache, cacheKey string, repo *repo_model.Repository, revision string) {
203-
ctx := graceful.GetManager().HammerContext()
204-
209+
func generateContributorStats(ctx context.Context, cache cache.StringCache, cacheKey string, repo *repo_model.Repository, revision string) (map[string]*ContributorData, error) {
205210
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
206211
if err != nil {
207-
_ = cache.PutJSON(cacheKey, fmt.Errorf("OpenRepository: %w", err), contributorStatsCacheTimeout)
208-
return
212+
return nil, err
209213
}
210214
defer closer.Close()
211215

212216
if len(revision) == 0 {
213217
revision = repo.DefaultBranch
214218
}
215-
extendedCommitStats, err := getExtendedCommitStats(gitRepo, revision)
219+
baseCommit, err := gitRepo.GetCommit(revision)
216220
if err != nil {
217-
_ = cache.PutJSON(cacheKey, fmt.Errorf("ExtendedCommitStats: %w", err), contributorStatsCacheTimeout)
218-
return
221+
return nil, err
222+
}
223+
extendedCommitStats, err := getExtendedCommitStats(ctx, repo.RepoPath(), baseCommit, revision)
224+
if err != nil {
225+
return nil, err
219226
}
220227
if len(extendedCommitStats) == 0 {
221-
_ = cache.PutJSON(cacheKey, fmt.Errorf("no commit stats returned for revision '%s'", revision), contributorStatsCacheTimeout)
222-
return
228+
return nil, fmt.Errorf("no commit stats returned for revision '%s'", revision)
223229
}
224230

225231
layout := time.DateOnly
@@ -300,9 +306,5 @@ func generateContributorStats(genDone chan struct{}, cache cache.StringCache, ca
300306
total.TotalCommits++
301307
}
302308

303-
_ = cache.PutJSON(cacheKey, contributorsCommitStats, contributorStatsCacheTimeout)
304-
generateLock.Delete(cacheKey)
305-
if genDone != nil {
306-
genDone <- struct{}{}
307-
}
309+
return contributorsCommitStats, nil
308310
}

0 commit comments

Comments
 (0)