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 }