github.com/0chain/gosdk@v1.17.11/zboxcore/sdk/moveworker.go (about) 1 package sdk 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io/ioutil" 8 "mime/multipart" 9 "net/http" 10 "strings" 11 "sync" 12 "time" 13 14 "github.com/0chain/errors" 15 thrown "github.com/0chain/errors" 16 "github.com/google/uuid" 17 18 "github.com/0chain/gosdk/constants" 19 "github.com/0chain/gosdk/core/common" 20 "github.com/0chain/gosdk/zboxcore/client" 21 "github.com/0chain/gosdk/zboxcore/fileref" 22 "github.com/0chain/gosdk/zboxcore/logger" 23 24 "github.com/0chain/gosdk/zboxcore/allocationchange" 25 "github.com/0chain/gosdk/zboxcore/blockchain" 26 l "github.com/0chain/gosdk/zboxcore/logger" 27 "github.com/0chain/gosdk/zboxcore/zboxutil" 28 ) 29 30 type MoveRequest struct { 31 allocationObj *Allocation 32 allocationID string 33 allocationTx string 34 sig string 35 blobbers []*blockchain.StorageNode 36 remotefilepath string 37 destPath string 38 ctx context.Context 39 ctxCncl context.CancelFunc 40 moveMask zboxutil.Uint128 41 maskMU *sync.Mutex 42 connectionID string 43 timestamp int64 44 Consensus 45 } 46 47 func (req *MoveRequest) getObjectTreeFromBlobber(blobber *blockchain.StorageNode) (fileref.RefEntity, error) { 48 return getObjectTreeFromBlobber(req.ctx, req.allocationID, req.allocationTx, req.sig, req.remotefilepath, blobber) 49 } 50 51 func (req *MoveRequest) moveBlobberObject( 52 blobber *blockchain.StorageNode, blobberIdx int) (refEntity fileref.RefEntity, err error) { 53 54 defer func() { 55 if err != nil { 56 req.maskMU.Lock() 57 // Removing blobber from mask 58 req.moveMask = req.moveMask.And(zboxutil.NewUint128(1).Lsh(uint64(blobberIdx)).Not()) 59 req.maskMU.Unlock() 60 } 61 }() 62 refEntity, err = req.getObjectTreeFromBlobber(req.blobbers[blobberIdx]) 63 if err != nil { 64 return nil, err 65 } 66 67 var resp *http.Response 68 var shouldContinue bool 69 var latestRespMsg string 70 var latestStatusCode int 71 for i := 0; i < 3; i++ { 72 err, shouldContinue = func() (err error, shouldContinue bool) { 73 body := new(bytes.Buffer) 74 formWriter := multipart.NewWriter(body) 75 76 err = formWriter.WriteField("connection_id", req.connectionID) 77 if err != nil { 78 return err, false 79 } 80 81 err = formWriter.WriteField("path", req.remotefilepath) 82 if err != nil { 83 return err, false 84 } 85 86 err = formWriter.WriteField("dest", req.destPath) 87 if err != nil { 88 return err, false 89 } 90 91 formWriter.Close() 92 93 var ( 94 httpreq *http.Request 95 respBody []byte 96 ctx context.Context 97 cncl context.CancelFunc 98 ) 99 100 httpreq, err = zboxutil.NewMoveRequest(blobber.Baseurl, req.allocationID, req.allocationTx, req.sig, body) 101 if err != nil { 102 l.Logger.Error(blobber.Baseurl, "Error creating rename request", err) 103 return 104 } 105 106 httpreq.Header.Add("Content-Type", formWriter.FormDataContentType()) 107 l.Logger.Info(httpreq.URL.Path) 108 ctx, cncl = context.WithTimeout(req.ctx, DefaultUploadTimeOut) 109 resp, err = zboxutil.Client.Do(httpreq.WithContext(ctx)) 110 defer cncl() 111 112 if err != nil { 113 logger.Logger.Error("Move: ", err) 114 return 115 } 116 117 if resp.Body != nil { 118 defer resp.Body.Close() 119 } 120 respBody, err = ioutil.ReadAll(resp.Body) 121 if err != nil { 122 logger.Logger.Error("Error: Resp ", err) 123 return 124 } 125 126 if resp.StatusCode == http.StatusOK { 127 l.Logger.Info(blobber.Baseurl, " "+req.remotefilepath, " moved.") 128 req.Consensus.Done() 129 return 130 } 131 132 latestRespMsg = string(respBody) 133 latestStatusCode = resp.StatusCode 134 135 if resp.StatusCode == http.StatusTooManyRequests { 136 logger.Logger.Error("Got too many request error") 137 var r int 138 r, err = zboxutil.GetRateLimitValue(resp) 139 if err != nil { 140 logger.Logger.Error(err) 141 return 142 } 143 time.Sleep(time.Duration(r) * time.Second) 144 shouldContinue = true 145 return 146 } 147 l.Logger.Error(blobber.Baseurl, "Response: ", string(respBody)) 148 err = errors.New("response_error", string(respBody)) 149 return 150 }() 151 152 if err != nil { 153 return 154 } 155 if shouldContinue { 156 continue 157 } 158 return 159 } 160 return nil, errors.New("unknown_issue", 161 fmt.Sprintf("last status code: %d, last response message: %s", latestStatusCode, latestRespMsg)) 162 } 163 164 func (req *MoveRequest) ProcessWithBlobbers() ([]fileref.RefEntity, []error) { 165 var pos uint64 166 numList := len(req.blobbers) 167 objectTreeRefs := make([]fileref.RefEntity, numList) 168 blobberErrors := make([]error, numList) 169 wg := &sync.WaitGroup{} 170 for i := req.moveMask; !i.Equals64(0); i = i.And(zboxutil.NewUint128(1).Lsh(pos).Not()) { 171 pos = uint64(i.TrailingZeros()) 172 wg.Add(1) 173 go func(blobberIdx int) { 174 defer wg.Done() 175 refEntity, err := req.moveBlobberObject(req.blobbers[blobberIdx], blobberIdx) 176 if err != nil { 177 blobberErrors[blobberIdx] = err 178 l.Logger.Error(err.Error()) 179 return 180 } 181 objectTreeRefs[blobberIdx] = refEntity 182 }(int(pos)) 183 } 184 wg.Wait() 185 return objectTreeRefs, blobberErrors 186 } 187 188 func (req *MoveRequest) ProcessMove() error { 189 defer req.ctxCncl() 190 191 wg := &sync.WaitGroup{} 192 var pos uint64 193 194 objectTreeRefs, blobberErrors := req.ProcessWithBlobbers() 195 196 if !req.isConsensusOk() { 197 err := zboxutil.MajorError(blobberErrors) 198 if err != nil { 199 return errors.New("move_failed", fmt.Sprintf("Move failed. %s", err.Error())) 200 } 201 202 return errors.New("consensus_not_met", 203 fmt.Sprintf("Move failed. Required consensus %d, got %d", 204 req.Consensus.consensusThresh, req.Consensus.consensus)) 205 } 206 207 writeMarkerMutex, err := CreateWriteMarkerMutex(client.GetClient(), req.allocationObj) 208 if err != nil { 209 return fmt.Errorf("Move failed: %s", err.Error()) 210 } 211 err = writeMarkerMutex.Lock(req.ctx, &req.moveMask, req.maskMU, 212 req.blobbers, &req.Consensus, 0, time.Minute, req.connectionID) 213 if err != nil { 214 return fmt.Errorf("Move failed: %s", err.Error()) 215 } 216 217 //Check if the allocation is to be repaired or rolled back 218 status, _, err := req.allocationObj.CheckAllocStatus() 219 if err != nil { 220 logger.Logger.Error("Error checking allocation status: ", err) 221 return fmt.Errorf("Move failed: %s", err.Error()) 222 } 223 defer writeMarkerMutex.Unlock(req.ctx, req.moveMask, req.blobbers, time.Minute, req.connectionID) //nolint: errcheck 224 225 if status == Repair { 226 logger.Logger.Info("Repairing allocation") 227 //TODO: Need status callback to call repair allocation 228 // err = req.allocationObj.RepairAlloc() 229 // if err != nil { 230 // return err 231 // } 232 } 233 if status != Commit { 234 return ErrRetryOperation 235 } 236 237 req.Consensus.Reset() 238 req.timestamp = int64(common.Now()) 239 activeBlobbers := req.moveMask.CountOnes() 240 wg.Add(activeBlobbers) 241 commitReqs := make([]*CommitRequest, activeBlobbers) 242 var c int 243 for i := req.moveMask; !i.Equals64(0); i = i.And(zboxutil.NewUint128(1).Lsh(pos).Not()) { 244 pos = uint64(i.TrailingZeros()) 245 246 moveChange := &allocationchange.MoveFileChange{ 247 DestPath: req.destPath, 248 ObjectTree: objectTreeRefs[pos], 249 } 250 moveChange.NumBlocks = 0 251 moveChange.Operation = constants.FileOperationMove 252 moveChange.Size = 0 253 commitReq := &CommitRequest{ 254 allocationID: req.allocationID, 255 allocationTx: req.allocationTx, 256 sig: req.sig, 257 blobber: req.blobbers[pos], 258 connectionID: req.connectionID, 259 wg: wg, 260 timestamp: req.timestamp, 261 } 262 // commitReq.change = moveChange 263 commitReq.changes = append(commitReq.changes, moveChange) 264 commitReqs[c] = commitReq 265 go AddCommitRequest(commitReq) 266 c++ 267 } 268 wg.Wait() 269 270 for _, commitReq := range commitReqs { 271 if commitReq.result != nil { 272 if commitReq.result.Success { 273 l.Logger.Info("Commit success", commitReq.blobber.Baseurl) 274 req.consensus++ 275 } else { 276 l.Logger.Info("Commit failed", commitReq.blobber.Baseurl, commitReq.result.ErrorMessage) 277 } 278 } else { 279 l.Logger.Info("Commit result not set", commitReq.blobber.Baseurl) 280 } 281 } 282 283 if !req.isConsensusOk() { 284 return errors.New("consensus_not_met", 285 fmt.Sprintf("Commit on move failed. Required consensus %d, got %d", 286 req.Consensus.consensusThresh, req.Consensus.consensus)) 287 } 288 return nil 289 } 290 291 type MoveOperation struct { 292 remotefilepath string 293 destPath string 294 ctx context.Context 295 ctxCncl context.CancelFunc 296 moveMask zboxutil.Uint128 297 maskMU *sync.Mutex 298 consensus Consensus 299 } 300 301 func (mo *MoveOperation) Process(allocObj *Allocation, connectionID string) ([]fileref.RefEntity, zboxutil.Uint128, error) { 302 mR := &MoveRequest{ 303 allocationObj: allocObj, 304 allocationID: allocObj.ID, 305 allocationTx: allocObj.Tx, 306 sig: allocObj.sig, 307 connectionID: connectionID, 308 blobbers: allocObj.Blobbers, 309 remotefilepath: mo.remotefilepath, 310 ctx: mo.ctx, 311 ctxCncl: mo.ctxCncl, 312 moveMask: mo.moveMask, 313 maskMU: mo.maskMU, 314 destPath: mo.destPath, 315 Consensus: Consensus{RWMutex: &sync.RWMutex{}}, 316 } 317 mR.Consensus.fullconsensus = mo.consensus.fullconsensus 318 mR.Consensus.consensusThresh = mo.consensus.consensusThresh 319 320 objectTreeRefs, blobberErrors := mR.ProcessWithBlobbers() 321 322 if !mR.Consensus.isConsensusOk() { 323 err := zboxutil.MajorError(blobberErrors) 324 if err != nil { 325 return nil, mR.moveMask, thrown.New("move_failed", fmt.Sprintf("Move failed. %s", err.Error())) 326 } 327 328 return nil, mR.moveMask, thrown.New("consensus_not_met", 329 fmt.Sprintf("Move failed. Required consensus %d, got %d", 330 mR.Consensus.consensusThresh, mR.Consensus.consensus)) 331 } 332 return objectTreeRefs, mR.moveMask, nil 333 } 334 335 func (mo *MoveOperation) buildChange(refs []fileref.RefEntity, uid uuid.UUID) []allocationchange.AllocationChange { 336 337 changes := make([]allocationchange.AllocationChange, len(refs)) 338 for idx, ref := range refs { 339 if ref == nil { 340 change := &allocationchange.EmptyFileChange{} 341 changes[idx] = change 342 continue 343 } 344 moveChange := &allocationchange.MoveFileChange{ 345 DestPath: mo.destPath, 346 ObjectTree: ref, 347 } 348 moveChange.NumBlocks = 0 349 moveChange.Operation = constants.FileOperationMove 350 moveChange.Size = 0 351 moveChange.Uuid = uid 352 changes[idx] = moveChange 353 } 354 return changes 355 } 356 357 func (mo *MoveOperation) Verify(a *Allocation) error { 358 359 if !a.CanMove() { 360 return constants.ErrFileOptionNotPermitted 361 } 362 363 if mo.remotefilepath == "" || mo.destPath == "" { 364 return errors.New("invalid_path", "Invalid path for move") 365 } 366 isabs := zboxutil.IsRemoteAbs(mo.remotefilepath) 367 if !isabs { 368 return errors.New("invalid_path", "Path should be valid and absolute") 369 } 370 371 err := ValidateRemoteFileName(mo.destPath) 372 373 if err != nil { 374 return err 375 } 376 return nil 377 } 378 379 func (mo *MoveOperation) Completed(allocObj *Allocation) { 380 381 } 382 383 func (mo *MoveOperation) Error(allocObj *Allocation, consensus int, err error) { 384 385 } 386 387 func NewMoveOperation(remotePath string, destPath string, moveMask zboxutil.Uint128, maskMU *sync.Mutex, consensusTh int, fullConsensus int, ctx context.Context) *MoveOperation { 388 mo := &MoveOperation{} 389 mo.remotefilepath = zboxutil.RemoteClean(remotePath) 390 if destPath != "/" { 391 destPath = strings.TrimSuffix(destPath, "/") 392 } 393 mo.destPath = destPath 394 mo.moveMask = moveMask 395 mo.maskMU = maskMU 396 mo.consensus.consensusThresh = consensusTh 397 mo.consensus.fullconsensus = fullConsensus 398 mo.ctx, mo.ctxCncl = context.WithCancel(ctx) 399 return mo 400 }