Skip to content

Commit 7494f80

Browse files
committed
Add concurrency in content API
Signed-off-by: Hariom Verma <[email protected]>
1 parent 65161bc commit 7494f80

File tree

3 files changed

+134
-32
lines changed

3 files changed

+134
-32
lines changed

main.go

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"path"
1414
"path/filepath"
1515
"regexp"
16+
"sort"
1617
"strings"
1718
"syscall"
1819

@@ -503,7 +504,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
503504
return
504505
}
505506
if treeEntry.Mode.IsFile() {
506-
var fileContent []*utils.Content
507+
var fileContent []utils.Content
507508
blob, err := object.GetBlob(repo.Storer, treeEntry.Hash)
508509
if err != nil {
509510
http.Error(w, err.Error(), http.StatusNotFound)
@@ -512,7 +513,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
512513
if err != nil {
513514
http.Error(w, err.Error(), http.StatusNotFound)
514515
}
515-
fileContent = append(fileContent, fc)
516+
fileContent = append(fileContent, *fc)
516517

517518
if body.IncludeLastCommit {
518519
pathCommitId, err := utils.LastCommitForPath(RepoPath, body.RefId, fileContent[0].Path)
@@ -550,50 +551,67 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
550551
}
551552
}
552553

553-
var treeContents []*utils.Content
554+
var treeContents []utils.Content
554555
pageRes, err := utils.PaginateTreeContentResponse(tree, body.Pagination, 100, body.Path, func(treeContent utils.Content) error {
555-
treeContents = append(treeContents, &treeContent)
556+
treeContents = append(treeContents, treeContent)
556557
return nil
557558
})
558559
if err != nil {
559560
http.Error(w, err.Error(), http.StatusBadRequest)
560561
return
561562
}
562563

564+
var resContents []utils.Content
565+
563566
if body.IncludeLastCommit {
564-
for i := range treeContents {
565-
pathCommitId, err := utils.LastCommitForPath(RepoPath, body.RefId, treeContents[i].Path)
566-
if err != nil {
567-
http.Error(w, err.Error(), http.StatusBadRequest)
568-
return
569-
}
570-
pathCommitHash := plumbing.NewHash(pathCommitId)
571-
pathCommitObject, err := object.GetCommit(repo.Storer, pathCommitHash)
572-
if err != nil {
573-
http.Error(w, err.Error(), http.StatusNotFound)
574-
return
575-
}
576-
treeContents[i].LastCommit, err = utils.GrabCommit(*pathCommitObject)
577-
if err != nil {
578-
http.Error(w, err.Error(), http.StatusBadRequest)
579-
return
567+
errc := make(chan error, len(treeContents))
568+
done := make(chan struct{})
569+
defer close(errc)
570+
defer close(done)
571+
572+
inputCh := utils.PrepareTreeContentPipeline(treeContents, done)
573+
574+
mergeInput := make([]<-chan utils.Content, len(treeContents))
575+
576+
for i := 0; i < len(treeContents); i++ {
577+
mergeInput[i] = utils.GetLastCommit(inputCh, RepoPath, repo.Storer, body.RefId, errc, done)
578+
}
579+
580+
final := utils.MergeContentChannel(done, mergeInput...)
581+
582+
go func() {
583+
for tc := range final {
584+
select {
585+
case <-done:
586+
return
587+
default:
588+
resContents = append(resContents, tc)
589+
}
580590
}
591+
errc <- nil
592+
}()
593+
594+
if err := <-errc; err != nil {
595+
http.Error(w, err.Error(), http.StatusBadRequest)
596+
return
581597
}
598+
} else {
599+
resContents = treeContents
582600
}
583601

584-
var sortedTREEContents []*utils.Content
585-
var sortedBLOBContents []*utils.Content
586-
for _, tc := range treeContents {
587-
if tc.Type == "TREE" {
588-
sortedTREEContents = append(sortedTREEContents, tc)
589-
} else {
590-
sortedBLOBContents = append(sortedBLOBContents, tc)
602+
/* Sort the contents */
603+
sort.Slice(resContents[:], func(i, j int) bool {
604+
switch strings.Compare(resContents[i].Type, resContents[j].Type) {
605+
case -1:
606+
return false
607+
case 1:
608+
return true
591609
}
592-
}
593-
sortedTreeContents := append(sortedTREEContents, sortedBLOBContents...)
610+
return resContents[i].Name < resContents[j].Name
611+
})
594612

595613
contentResponse := utils.ContentResponse{
596-
Content: sortedTreeContents,
614+
Content: resContents,
597615
Pagination: pageRes,
598616
}
599617
contentResponseJson, err := json.Marshal(contentResponse)

utils/common.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package utils
22

3-
import "encoding/binary"
3+
import (
4+
"encoding/binary"
5+
)
46

57
func UInt64ToBytes(id uint64) []byte {
68
bz := make([]byte, 8)

utils/content.go

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ import (
44
"encoding/base64"
55
"fmt"
66
"io/ioutil"
7+
"sync"
78

9+
"github.com/gitopia/go-git/v5/plumbing"
810
"github.com/gitopia/go-git/v5/plumbing/object"
11+
"github.com/gitopia/go-git/v5/storage"
912
)
1013

1114
type ContentType int
@@ -29,7 +32,7 @@ type ContentRequestBody struct {
2932
}
3033

3134
type ContentResponse struct {
32-
Content []*Content `json:"content,omitempty"`
35+
Content []Content `json:"content,omitempty"`
3336
Pagination *PageResponse `json:"pagination,omitempty"`
3437
}
3538

@@ -183,3 +186,82 @@ func PaginateTreeContentResponse(
183186

184187
return res, nil
185188
}
189+
190+
func PrepareTreeContentPipeline(treeContents []Content, done chan struct{}) <-chan Content {
191+
out := make(chan Content)
192+
193+
go func() {
194+
defer close(out)
195+
for i := range treeContents {
196+
select {
197+
case out <- treeContents[i]:
198+
case <-done:
199+
return
200+
}
201+
}
202+
}()
203+
204+
return out
205+
}
206+
207+
func GetLastCommit(treeEntry <-chan Content, RepoPath string, repoStorer storage.Storer, refId string, errc chan<- error, done <-chan struct{}) <-chan Content {
208+
out := make(chan Content)
209+
210+
go func() {
211+
defer close(out)
212+
for te := range treeEntry {
213+
select {
214+
case <-done:
215+
return
216+
default:
217+
pathCommitId, err := LastCommitForPath(RepoPath, refId, te.Path)
218+
if err != nil {
219+
errc <- err
220+
return
221+
}
222+
pathCommitHash := plumbing.NewHash(pathCommitId)
223+
pathCommitObject, err := object.GetCommit(repoStorer, pathCommitHash)
224+
if err != nil {
225+
errc <- err
226+
return
227+
}
228+
te.LastCommit, err = GrabCommit(*pathCommitObject)
229+
if err != nil {
230+
errc <- err
231+
return
232+
}
233+
out <- te
234+
}
235+
}
236+
}()
237+
238+
return out
239+
}
240+
241+
func MergeContentChannel(done chan struct{}, cs ...<-chan Content) <-chan Content {
242+
out := make(chan Content)
243+
wg := sync.WaitGroup{}
244+
245+
output := func(c <-chan Content) {
246+
defer wg.Done()
247+
for i := range c {
248+
select {
249+
case out <- i:
250+
case <-done:
251+
return
252+
}
253+
}
254+
}
255+
256+
wg.Add(len(cs))
257+
for _, ch := range cs {
258+
go output(ch)
259+
}
260+
261+
go func() {
262+
wg.Wait()
263+
close(out)
264+
}()
265+
266+
return out
267+
}

0 commit comments

Comments
 (0)