@@ -204,6 +204,20 @@ func runCmd(ctx context.Context, dir string, env []string, name string, args ...
204204 return nil
205205}
206206
207+ // runWithSemaphore runs function after waiting at semaphore for concurrency control
208+ func runWithSemaphore (ctx context.Context , f func () (any , error )) (any , error ) {
209+ select {
210+ case semaphore <- struct {}{}:
211+ defer func () { <- semaphore }()
212+ logger .DebugContext (ctx , "Concurrent requests" , slog .Int ("count" , len (semaphore )))
213+
214+ return f ()
215+ case <- ctx .Done ():
216+ logger .WarnContext (ctx , "Request cancelled while waiting for semaphore" )
217+ return nil , ctx .Err ()
218+ }
219+ }
220+
207221func isLocalRequest (r * http.Request ) bool {
208222 host , _ , err := net .SplitHostPort (r .RemoteAddr )
209223 if err != nil {
@@ -305,7 +319,9 @@ func marshalResponse(r *http.Request, m proto.Message) ([]byte, error) {
305319
306320func doFetch (ctx context.Context , w http.ResponseWriter , repoURL string , forceUpdate bool ) error {
307321 _ , err , _ := gFetch .Do (repoURL , func () (any , error ) {
308- return nil , FetchRepo (ctx , repoURL , forceUpdate )
322+ return runWithSemaphore (ctx , func () (any , error ) {
323+ return nil , FetchRepo (ctx , repoURL , forceUpdate )
324+ })
309325 })
310326 if err != nil {
311327 logger .ErrorContext (ctx , "Error fetching blob" , slog .Any ("error" , err ))
@@ -341,11 +357,13 @@ func getFreshRepo(ctx context.Context, w http.ResponseWriter, repoURL string, fo
341357 }
342358
343359 repoAny , err , _ := gLoad .Do (repoPath , func () (any , error ) {
344- repoLock := GetRepoLock (repoURL )
345- repoLock .RLock ()
346- defer repoLock .RUnlock ()
360+ return runWithSemaphore (ctx , func () (any , error ) {
361+ repoLock := GetRepoLock (repoURL )
362+ repoLock .RLock ()
363+ defer repoLock .RUnlock ()
347364
348- return LoadRepository (ctx , repoPath )
365+ return LoadRepository (ctx , repoPath )
366+ })
349367 })
350368 if err != nil {
351369 logger .ErrorContext (ctx , "Failed to load repository" , slog .Any ("error" , err ))
@@ -568,25 +586,16 @@ func gitHandler(w http.ResponseWriter, r *http.Request) {
568586 ctx := context .WithValue (r .Context (), urlKey , repoURL )
569587 logger .InfoContext (ctx , "Received request: /git" , slog .Bool ("forceUpdate" , forceUpdate ), slog .String ("remoteAddr" , r .RemoteAddr ))
570588
571- select {
572- case semaphore <- struct {}{}:
573- defer func () { <- semaphore }()
574- case <- ctx .Done ():
575- logger .WarnContext (ctx , "Request cancelled while waiting for semaphore" )
576- http .Error (w , "Server context cancelled" , http .StatusServiceUnavailable )
577-
578- return
579- }
580- logger .DebugContext (ctx , "Concurrent requests" , slog .Int ("count" , len (semaphore )))
581-
582589 // Fetch repo first
583590 if err := doFetch (ctx , w , repoURL , forceUpdate ); err != nil {
584591 return
585592 }
586593
587594 // Archive repo
588595 fileDataAny , err , _ := gArchive .Do (repoURL , func () (any , error ) {
589- return ArchiveRepo (ctx , repoURL )
596+ return runWithSemaphore (ctx , func () (any , error ) {
597+ return ArchiveRepo (ctx , repoURL )
598+ })
590599 })
591600 if err != nil {
592601 logger .ErrorContext (ctx , "Error archiving blob" , slog .Any ("error" , err ))
@@ -626,17 +635,6 @@ func cacheHandler(w http.ResponseWriter, r *http.Request) {
626635 ctx := context .WithValue (r .Context (), urlKey , repoURL )
627636 logger .InfoContext (ctx , "Received request: /cache" )
628637
629- select {
630- case semaphore <- struct {}{}:
631- defer func () { <- semaphore }()
632- case <- ctx .Done ():
633- logger .WarnContext (ctx , "Request cancelled while waiting for semaphore" )
634- http .Error (w , "Server context cancelled" , http .StatusServiceUnavailable )
635-
636- return
637- }
638- logger .DebugContext (ctx , "Concurrent requests" , slog .Int ("count" , len (semaphore )))
639-
640638 if _ , err := getFreshRepo (ctx , w , repoURL , body .GetForceUpdate ()); err != nil {
641639 return
642640 }
@@ -687,17 +685,6 @@ func affectedCommitsHandler(w http.ResponseWriter, r *http.Request) {
687685 slog .Bool ("considerAllBranches" , considerAllBranches ),
688686 )
689687
690- select {
691- case semaphore <- struct {}{}:
692- defer func () { <- semaphore }()
693- case <- ctx .Done ():
694- logger .WarnContext (ctx , "Request cancelled while waiting for semaphore" )
695- http .Error (w , "Server context cancelled" , http .StatusServiceUnavailable )
696-
697- return
698- }
699- logger .DebugContext (ctx , "Concurrent requests" , slog .Int ("count" , len (semaphore )))
700-
701688 repo , err := getFreshRepo (ctx , w , repoURL , body .GetForceUpdate ())
702689 if err != nil {
703690 return
0 commit comments