@@ -17,6 +17,7 @@ package odm
1717
1818import (
1919 "encoding/json"
20+ "errors"
2021 "io"
2122 "io/ioutil"
2223 "mime/multipart"
@@ -34,44 +35,41 @@ import (
3435 "github.com/cheggaaa/pb"
3536)
3637
37- type TaskNewResponse struct {
38- UUID string `json:"uuid"`
39- Error string `json:"error"`
38+ type fileUpload struct {
39+ filename string
40+ retries int
4041}
4142
42- // Run processes a dataset
43- func Run (files []string , options []Option , node Node , outputPath string ) {
44- var err error
45- var bar * pb.ProgressBar
43+ type fileUploadResult struct {
44+ filename string
45+ err error
46+ retries int
47+ }
4648
49+ func singleUpload (node Node , files []string , jsonOptions []byte ) TaskNewResponse {
4750 var f * os.File
4851 var fi os.FileInfo
49-
50- var totalBytes int64
52+ var err error
53+ var bar * pb.ProgressBar
54+ var res TaskNewResponse
5155
5256 showProgress := ! logger .QuietFlag
5357
54- // Calculate total upload size
55- for _ , file := range files {
56- if fi , err = os .Stat (file ); err != nil {
57- logger .Error (err )
58+ if showProgress {
59+ var totalBytes int64
60+
61+ // Calculate total upload size
62+ for _ , file := range files {
63+ if fi , err = os .Stat (file ); err != nil {
64+ logger .Error (err )
65+ }
66+ totalBytes += fi .Size ()
5867 }
59- totalBytes += fi .Size ()
60- f .Close ()
61- }
6268
63- if showProgress {
6469 bar = pb .New64 (totalBytes ).SetUnits (pb .U_BYTES ).SetRefreshRate (time .Millisecond * 10 )
6570 bar .Start ()
6671 }
6772
68- // Convert options to JSON
69- jsonOptions , err := json .Marshal (options )
70- if err != nil {
71- logger .Error (err )
72- }
73-
74- // Setup pipe
7573 r , w := io .Pipe ()
7674 mpw := multipart .NewWriter (w )
7775
@@ -122,18 +120,128 @@ func Run(files []string, options []Option, node Node, outputPath string) {
122120 logger .Error (err )
123121 }
124122
125- res := TaskNewResponse {}
123+ if showProgress {
124+ bar .Finish ()
125+ }
126+
126127 if err := json .Unmarshal (body , & res ); err != nil {
127128 logger .Error (err )
128129 }
129130
130- // Handle error response from API
131+ return res
132+ }
133+
134+ func uploadWorker (id int , node Node , uuid string , barPool * pb.Pool , filesToProcess <- chan fileUpload , results chan <- fileUploadResult ) {
135+ var bar * pb.ProgressBar
136+
137+ if barPool != nil {
138+ bar = pb .New64 (0 ).SetUnits (pb .U_BYTES ).SetRefreshRate (time .Millisecond * 10 )
139+ barPool .Add (bar )
140+ }
141+
142+ for f := range filesToProcess {
143+ results <- fileUploadResult {f .filename , node .TaskNewUpload (f .filename , uuid , bar ), f .retries }
144+ }
145+ }
146+
147+ func chunkedUpload (node Node , files []string , jsonOptions []byte , parallelUploads int ) TaskNewResponse {
148+ var err error
149+ var barPool * pb.Pool
150+ var mainBar * pb.ProgressBar
151+ var res TaskNewResponse
152+
153+ showProgress := ! logger .QuietFlag
154+
155+ // Invoke /task/new/init
156+ res = node .TaskNewInit (jsonOptions )
131157 if res .Error != "" {
132- logger .Error (res . Error )
158+ logger .Error (err )
133159 }
134160
135161 if showProgress {
136- bar .Finish ()
162+ barPool = pb .NewPool ()
163+ }
164+
165+ // Create workers
166+ filesToProcess := make (chan fileUpload , len (files ))
167+ results := make (chan fileUploadResult , len (files ))
168+
169+ for w := 1 ; w <= parallelUploads ; w ++ {
170+ go uploadWorker (w , node , res .UUID , barPool , filesToProcess , results )
171+ }
172+
173+ if barPool != nil {
174+ barPool .Start ()
175+
176+ mainBar = pb .New (len (files )).SetUnits (pb .U_NO ).SetRefreshRate (time .Millisecond * 10 )
177+ mainBar .Format ("[\x00 #\x00 \x00 _\x00 ]" )
178+ mainBar .Prefix ("Files Uploaded:" )
179+ mainBar .Start ()
180+ }
181+
182+ // Fill queue
183+ for _ , file := range files {
184+ filesToProcess <- fileUpload {file , 0 }
185+ }
186+
187+ // Wait
188+ MaxRetries := 10
189+ filesLeft := len (files )
190+ for filesLeft > 0 {
191+ fur := <- results
192+
193+ if fur .err != nil {
194+ if fur .retries < MaxRetries {
195+ // Retry
196+ filesToProcess <- fileUpload {fur .filename , fur .retries + 1 }
197+ } else {
198+ logger .Error (errors .New ("Cannot upload " + fur .filename + ", exceeded max retries (" + string (MaxRetries ) + ")" ))
199+ }
200+ } else {
201+ filesLeft --
202+ if mainBar != nil {
203+ mainBar .Set (len (files ) - filesLeft )
204+ }
205+ }
206+ }
207+ close (filesToProcess )
208+
209+ if barPool != nil {
210+ barPool .Stop ()
211+ }
212+ if mainBar != nil {
213+ mainBar .Finish ()
214+ }
215+
216+ // Commit
217+ res = node .TaskNewCommit (res .UUID )
218+ if res .Error != "" {
219+ logger .Error (res .Error )
220+ }
221+
222+ return res
223+ }
224+
225+ // Run processes a dataset
226+ func Run (files []string , options []Option , node Node , outputPath string , parallelConnections int ) {
227+ var err error
228+
229+ // Convert options to JSON
230+ jsonOptions , err := json .Marshal (options )
231+ if err != nil {
232+ logger .Error (err )
233+ }
234+
235+ var res TaskNewResponse
236+ if parallelConnections <= 1 {
237+ res = singleUpload (node , files , jsonOptions )
238+ } else {
239+ res = chunkedUpload (node , files , jsonOptions , parallelConnections )
240+ }
241+
242+ // Handle error response from API
243+ if res .Error != "" {
244+ logger .Error (res .Error )
137245 }
138246
139247 // We should have a UUID
0 commit comments