github.com/Files-com/files-sdk-go/v3@v3.1.81/file/retrytransfers.go (about) 1 package file 2 3 import ( 4 "context" 5 "strings" 6 "time" 7 8 "github.com/Files-com/files-sdk-go/v3/file/status" 9 "github.com/Files-com/files-sdk-go/v3/lib/direction" 10 "github.com/bradfitz/iter" 11 ) 12 13 type RetryPolicy struct { 14 Type RetryPolicyType 15 RetryCount int 16 Backoff int 17 } 18 19 func (p RetryPolicy) WaitSec(retry int) time.Duration { 20 if p.Backoff == 0 { 21 p.Backoff = 2 22 } 23 return time.Second * time.Duration(p.Backoff*retry) 24 } 25 26 type RetryPolicyType string 27 28 const ( 29 RetryAll = RetryPolicyType("RetryAll") 30 RetryUnfinished = RetryPolicyType("RetryUnfinished") 31 ) 32 33 func RetryByPolicy(ctx context.Context, job *Job, policy RetryPolicy, signalEvents bool) { 34 switch policy.Type { 35 case RetryAll: 36 RetryByStatus(ctx, job, signalEvents, policy, status.Included...) 37 case RetryUnfinished: 38 RetryByStatus(ctx, job, signalEvents, policy, append(status.Running, []status.GetStatus{status.Errored, status.Canceled}...)...) 39 } 40 } 41 42 func RetryByStatus(ctx context.Context, job *Job, signalEvents bool, policy RetryPolicy, s ...status.GetStatus) { 43 for i := range iter.N(policy.RetryCount) { 44 switch job.Direction { 45 case direction.DownloadType: 46 retryDownload(ctx, job, signalEvents, s) 47 case direction.UploadType: 48 retryUpload(ctx, job, signalEvents, s) 49 default: 50 panic("invalid direction") 51 } 52 if len(job.Sub(s...).Statuses) > 0 && i+1 != policy.RetryCount { 53 job.Logger.Printf("retry (%v): backing off %v sec", i+1, policy.WaitSec(i)) 54 time.Sleep(policy.WaitSec(i)) 55 } else { 56 return 57 } 58 } 59 } 60 61 func retryUpload(ctx context.Context, job *Job, signalEvents bool, s []status.GetStatus) { 62 onComplete := make(chan *UploadStatus) 63 defer close(onComplete) 64 enqueueByStatus(ctx, job, signalEvents, 65 func(s IFile, jobCxt context.Context) { 66 job.UpdateStatus(status.Retrying, s.(*UploadStatus), nil) 67 enqueueUpload(jobCxt, job, s.(*UploadStatus), onComplete) 68 }, func() { 69 <-onComplete 70 }, 71 s..., 72 ) 73 } 74 75 func retryDownload(ctx context.Context, job *Job, signalEvents bool, s []status.GetStatus) { 76 onComplete := make(chan *DownloadStatus) 77 defer close(onComplete) 78 enqueueByStatus(ctx, job, signalEvents, 79 func(s IFile, jobCxt context.Context) { 80 job.UpdateStatus(status.Retrying, s.(*DownloadStatus), nil) 81 enqueueDownload(jobCxt, job, s.(*DownloadStatus), onComplete) 82 }, func() { 83 <-onComplete 84 }, 85 s..., 86 ) 87 } 88 89 func enqueueByStatus(ctx context.Context, job *Job, signalEvents bool, enqueue func(IFile, context.Context), waitForComplete func(), s ...status.GetStatus) { 90 if job.Count(s...) == 0 { 91 return 92 } 93 jobCtx := job.WithContext(ctx) 94 95 count := 0 96 97 if signalEvents { 98 job.ClearCalled() 99 job.Start(false) 100 job.Scan() 101 } 102 103 files := job.Sub(s...).Statuses 104 105 var types []string 106 for _, st := range s { 107 types = append(types, st.Status().String()) 108 } 109 job.Logger.Printf("retrying %v files (%v)", strings.Join(types, ", "), len(files)) 110 111 for _, file := range files { 112 if job.FilesManager.WaitWithContext(jobCtx) { 113 count += 1 114 go enqueue(file, jobCtx) 115 } 116 } 117 if signalEvents { 118 job.EndScan() 119 } 120 for range iter.N(count) { 121 waitForComplete() 122 } 123 if signalEvents { 124 job.Finish() 125 } 126 }