@@ -16,6 +16,8 @@ import (
1616	"code.gitea.io/gitea/modules/json" 
1717	"code.gitea.io/gitea/modules/log" 
1818	"code.gitea.io/gitea/modules/proxy" 
19+ 
20+ 	"golang.org/x/sync/errgroup" 
1921)
2022
2123const  httpBatchSize  =  20 
@@ -114,6 +116,7 @@ func (c *HTTPClient) Upload(ctx context.Context, objects []Pointer, callback Upl
114116	return  c .performOperation (ctx , objects , nil , callback )
115117}
116118
119+ // performOperation takes a slice of LFS object pointers, batches them, and performs the upload/download operations concurrently in each batch 
117120func  (c  * HTTPClient ) performOperation (ctx  context.Context , objects  []Pointer , dc  DownloadCallback , uc  UploadCallback ) error  {
118121	if  len (objects ) ==  0  {
119122		return  nil 
@@ -134,71 +137,84 @@ func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc
134137		return  fmt .Errorf ("TransferAdapter not found: %s" , result .Transfer )
135138	}
136139
140+ 	errGroup , groupCtx  :=  errgroup .WithContext (context .Background ())
137141	for  _ , object  :=  range  result .Objects  {
138- 		if  object .Error  !=  nil  {
139- 			log .Trace ("Error on object %v: %v" , object .Pointer , object .Error )
140- 			if  uc  !=  nil  {
141- 				if  _ , err  :=  uc (object .Pointer , object .Error ); err  !=  nil  {
142- 					return  err 
143- 				}
144- 			} else  {
145- 				if  err  :=  dc (object .Pointer , nil , object .Error ); err  !=  nil  {
146- 					return  err 
147- 				}
148- 			}
149- 			continue 
150- 		}
142+ 		func (groupCtx  context.Context , object  * ObjectResponse , dc  DownloadCallback , uc  UploadCallback , transferAdapter  TransferAdapter ) {
143+ 			errGroup .Go (func () error  {
144+ 				err  :=  performSingleOperation (groupCtx , object , dc , uc , transferAdapter )
145+ 				return  err 
146+ 			})
147+ 		}(groupCtx , object , dc , uc , transferAdapter )
148+ 	}
151149
150+ 	// only the first error is returned, preserving legacy behavior before concurrency 
151+ 	return  errGroup .Wait ()
152+ }
153+ 
154+ // performSingleOperation performs an LFS upload or download operation on a single object 
155+ func  performSingleOperation (ctx  context.Context , object  * ObjectResponse , dc  DownloadCallback , uc  UploadCallback , transferAdapter  TransferAdapter ) error  {
156+ 	if  object .Error  !=  nil  {
157+ 		log .Trace ("Error on object %v: %v" , object .Pointer , object .Error )
152158		if  uc  !=  nil  {
153- 			if  len (object .Actions ) ==  0  {
154- 				log .Trace ("%v already present on server" , object .Pointer )
155- 				continue 
159+ 			if  _ , err  :=  uc (object .Pointer , object .Error ); err  !=  nil  {
160+ 				return  err 
156161			}
162+ 		}
163+ 		err  :=  dc (object .Pointer , nil , object .Error )
164+ 		if  errors .Is (object .Error , ErrObjectNotExist ) {
165+ 			log .Warn ("Ignoring missing upstream LFS object %-v: %v" , object .Pointer , err )
166+ 			return  nil 
167+ 		}
168+ 		return  err 
169+ 	}
157170
158- 			 link ,  ok   :=   object . Actions [ "upload" ] 
159- 			 if  ! ok  {
160- 				 log .Debug ("%+v " , object )
161- 				 return  errors . New ( "missing action 'upload'" ) 
162- 			 }
171+ 	if   uc   !=   nil  { 
172+ 		if  len ( object . Actions )  ==   0  {
173+ 			log .Trace ("%v already present on server " , object . Pointer )
174+ 			return  nil 
175+ 		}
163176
164- 			 content ,  err  :=  uc ( object .Pointer ,  nil ) 
165- 			 if  err   !=   nil  {
166- 				 return  err 
167- 			 }
177+ 		link ,  ok  :=  object .Actions [ "upload" ] 
178+ 		if  ! ok  {
179+ 			return  errors . New ( "missing action 'upload'" ) 
180+ 		}
168181
169- 			 err  =   transferAdapter . Upload ( ctx ,  link ,  object .Pointer , content )
170- 			 if  err  !=  nil  {
171- 				 return  err 
172- 			 }
182+ 		content ,  err  :=   uc ( object .Pointer , nil )
183+ 		if  err  !=  nil  {
184+ 			return  err 
185+ 		}
173186
174- 			link , ok  =  object .Actions ["verify" ]
175- 			if  ok  {
176- 				if  err  :=  transferAdapter .Verify (ctx , link , object .Pointer ); err  !=  nil  {
177- 					return  err 
178- 				}
179- 			}
180- 		} else  {
181- 			link , ok  :=  object .Actions ["download" ]
182- 			if  ! ok  {
183- 				// no actions block in response, try legacy response schema 
184- 				link , ok  =  object .Links ["download" ]
185- 			}
186- 			if  ! ok  {
187- 				log .Debug ("%+v" , object )
188- 				return  errors .New ("missing action 'download'" )
189- 			}
187+ 		err  =  transferAdapter .Upload (ctx , link , object .Pointer , content )
188+ 		if  err  !=  nil  {
189+ 			return  err 
190+ 		}
190191
191- 			content , err  :=  transferAdapter .Download (ctx , link )
192- 			if  err  !=  nil  {
192+ 		link , ok  =  object .Actions ["verify" ]
193+ 		if  ok  {
194+ 			if  err  :=  transferAdapter .Verify (ctx , link , object .Pointer ); err  !=  nil  {
193195				return  err 
194196			}
197+ 		}
198+ 	} else  {
199+ 		link , ok  :=  object .Actions ["download" ]
200+ 		if  ! ok  {
201+ 			// no actions block in response, try legacy response schema 
202+ 			link , ok  =  object .Links ["download" ]
203+ 		}
204+ 		if  ! ok  {
205+ 			log .Debug ("%+v" , object )
206+ 			return  errors .New ("missing action 'download'" )
207+ 		}
195208
196- 			 if  err  :=  dc ( object . Pointer ,  content ,  nil );  err   !=   nil  { 
197- 				 return  err 
198- 			} 
209+ 		content ,  err  :=  transferAdapter . Download ( ctx ,  link ) 
210+ 		if  err   !=   nil  { 
211+ 			return   err 
199212		}
200- 	}
201213
214+ 		if  err  :=  dc (object .Pointer , content , nil ); err  !=  nil  {
215+ 			return  err 
216+ 		}
217+ 	}
202218	return  nil 
203219}
204220
0 commit comments