github.com/0chain/gosdk@v1.17.11/zboxcore/sdk/chunked_upload_progress_storer.go (about)

     1  package sdk
     2  
     3  import (
     4  	"container/heap"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"os"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/0chain/gosdk/core/common"
    13  	"github.com/0chain/gosdk/core/sys"
    14  	"github.com/0chain/gosdk/zboxcore/logger"
    15  	"github.com/0chain/gosdk/zboxcore/zboxutil"
    16  )
    17  
    18  // ChunkedUploadProgressStorer load and save upload progress
    19  type ChunkedUploadProgressStorer interface {
    20  	// Load load upload progress by id
    21  	Load(id string) *UploadProgress
    22  	// Save save upload progress
    23  	Save(up UploadProgress)
    24  	// Remove remove upload progress by id
    25  	Remove(id string) error
    26  	// Update update upload progress
    27  	Update(id string, chunkIndex int, upMask zboxutil.Uint128)
    28  }
    29  
    30  // fsChunkedUploadProgressStorer load and save upload progress in file system
    31  type fsChunkedUploadProgressStorer struct {
    32  	sync.Mutex
    33  	isRemoved  bool
    34  	up         UploadProgress
    35  	queue      queue
    36  	next       int
    37  	uploadMask zboxutil.Uint128
    38  }
    39  
    40  type queue []int
    41  
    42  func (pq queue) Len() int { return len(pq) }
    43  
    44  func (pq queue) Less(i, j int) bool {
    45  	return pq[i] < pq[j]
    46  }
    47  
    48  func (pq queue) Swap(i, j int) {
    49  	pq[i], pq[j] = pq[j], pq[i]
    50  }
    51  
    52  func (pq *queue) Push(x interface{}) {
    53  	*pq = append(*pq, x.(int))
    54  }
    55  
    56  func (pq *queue) Pop() interface{} {
    57  	old := *pq
    58  	n := len(old)
    59  	item := old[n-1]
    60  	*pq = old[0 : n-1]
    61  	return item
    62  }
    63  
    64  func createFsChunkedUploadProgress(ctx context.Context) *fsChunkedUploadProgressStorer {
    65  	up := &fsChunkedUploadProgressStorer{
    66  		queue: make(queue, 0),
    67  	}
    68  	heap.Init(&up.queue)
    69  	go saveProgress(ctx, up)
    70  	return up
    71  }
    72  
    73  func saveProgress(ctx context.Context, fs *fsChunkedUploadProgressStorer) {
    74  	tc := time.NewTicker(2 * time.Second)
    75  	defer tc.Stop()
    76  	for {
    77  		select {
    78  		case <-ctx.Done():
    79  			return
    80  		case <-tc.C:
    81  			fs.Lock()
    82  			if fs.isRemoved {
    83  				fs.Unlock()
    84  				return
    85  			}
    86  			if len(fs.queue) > 0 && fs.next == fs.queue[0] {
    87  				for len(fs.queue) > 0 && fs.next == fs.queue[0] {
    88  					fs.up.ChunkIndex = fs.queue[0]
    89  					heap.Pop(&fs.queue)
    90  					fs.next += fs.up.ChunkNumber
    91  				}
    92  				fs.Unlock()
    93  				fs.Save(fs.up)
    94  			} else {
    95  				fs.Unlock()
    96  			}
    97  		}
    98  	}
    99  }
   100  
   101  // Load load upload progress from file system
   102  func (fs *fsChunkedUploadProgressStorer) Load(progressID string) *UploadProgress {
   103  
   104  	progress := UploadProgress{}
   105  	buf, err := sys.Files.LoadProgress(progressID)
   106  
   107  	if errors.Is(err, os.ErrNotExist) {
   108  		return nil
   109  	}
   110  
   111  	if err := json.Unmarshal(buf, &progress); err != nil {
   112  		return nil
   113  	}
   114  
   115  	// if progress is not updated within 25 min, return nil
   116  	if !progress.LastUpdated.Within(25 * 60) {
   117  		sys.Files.Remove(progressID) //nolint:errcheck
   118  		return nil
   119  	}
   120  
   121  	return &progress
   122  }
   123  
   124  // Save save upload progress in file system
   125  func (fs *fsChunkedUploadProgressStorer) Save(up UploadProgress) {
   126  	fs.Lock()
   127  	defer fs.Unlock()
   128  	fs.up = up
   129  	fs.up.LastUpdated = common.Now()
   130  
   131  	if fs.isRemoved {
   132  		return
   133  	}
   134  	if fs.next == 0 {
   135  		if up.ChunkNumber == -1 {
   136  			fs.next = up.ChunkNumber - 1
   137  		} else {
   138  			fs.next = up.ChunkIndex + up.ChunkNumber
   139  		}
   140  	}
   141  	fs.up.UploadMask = fs.uploadMask
   142  	buf, err := json.Marshal(fs.up)
   143  	if err != nil {
   144  		logger.Logger.Error("[progress] save ", fs.up, err)
   145  		return
   146  	}
   147  	err = sys.Files.SaveProgress(fs.up.ID, buf, 0666)
   148  	if err != nil {
   149  		logger.Logger.Error("[progress] save ", fs.up, err)
   150  		return
   151  	}
   152  }
   153  
   154  func (fs *fsChunkedUploadProgressStorer) Update(id string, chunkIndex int, upMask zboxutil.Uint128) {
   155  	fs.Lock()
   156  	defer fs.Unlock()
   157  	if !fs.isRemoved {
   158  		fs.uploadMask = upMask
   159  		heap.Push(&fs.queue, chunkIndex)
   160  	}
   161  }
   162  
   163  // Remove remove upload progress from file system
   164  func (fs *fsChunkedUploadProgressStorer) Remove(progressID string) error {
   165  	fs.Lock()
   166  	defer fs.Unlock()
   167  	fs.isRemoved = true
   168  	err := sys.Files.RemoveProgress(progressID)
   169  	if err != nil {
   170  		if !errors.Is(err, os.ErrNotExist) {
   171  			return nil
   172  		}
   173  
   174  		return err
   175  	}
   176  
   177  	return nil
   178  }