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  }