@@ -16,6 +16,8 @@ import (
16
16
"code.gitea.io/gitea/modules/json"
17
17
"code.gitea.io/gitea/modules/log"
18
18
"code.gitea.io/gitea/modules/proxy"
19
+
20
+ "golang.org/x/sync/errgroup"
19
21
)
20
22
21
23
const httpBatchSize = 20
@@ -114,6 +116,7 @@ func (c *HTTPClient) Upload(ctx context.Context, objects []Pointer, callback Upl
114
116
return c .performOperation (ctx , objects , nil , callback )
115
117
}
116
118
119
+ // performOperation takes a slice of LFS object pointers, batches them, and performs the upload/download operations concurrently in each batch
117
120
func (c * HTTPClient ) performOperation (ctx context.Context , objects []Pointer , dc DownloadCallback , uc UploadCallback ) error {
118
121
if len (objects ) == 0 {
119
122
return nil
@@ -134,71 +137,84 @@ func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc
134
137
return fmt .Errorf ("TransferAdapter not found: %s" , result .Transfer )
135
138
}
136
139
140
+ errGroup , groupCtx := errgroup .WithContext (context .Background ())
137
141
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
+ }
151
149
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 )
152
158
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
156
161
}
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
+ }
157
170
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
+ }
163
176
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
+ }
168
181
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
+ }
173
186
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
+ }
190
191
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 {
193
195
return err
194
196
}
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
+ }
195
208
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
199
212
}
200
- }
201
213
214
+ if err := dc (object .Pointer , content , nil ); err != nil {
215
+ return err
216
+ }
217
+ }
202
218
return nil
203
219
}
204
220
0 commit comments