in component/azstorage/block_blob.go [344:415]
func (bb *BlockBlob) RenameFile(source string, target string, srcAttr *internal.ObjAttr) error {
log.Trace("BlockBlob::RenameFile : %s -> %s", source, target)
blobClient := bb.Container.NewBlockBlobClient(filepath.Join(bb.Config.prefixPath, source))
newBlobClient := bb.Container.NewBlockBlobClient(filepath.Join(bb.Config.prefixPath, target))
// not specifying source blob metadata, since passing empty metadata headers copies
// the source blob metadata to destination blob
copyResponse, err := newBlobClient.StartCopyFromURL(context.Background(), blobClient.URL(), &blob.StartCopyFromURLOptions{
Tier: bb.Config.defaultTier,
})
if err != nil {
serr := storeBlobErrToErr(err)
if serr == ErrFileNotFound {
//Ideally this case doesn't hit as we are checking for the existence of src
//before making the call for RenameFile
log.Err("BlockBlob::RenameFile : Src Blob doesn't Exist %s [%s]", source, err.Error())
return syscall.ENOENT
}
log.Err("BlockBlob::RenameFile : Failed to start copy of file %s [%s]", source, err.Error())
return err
}
var dstLMT *time.Time = copyResponse.LastModified
var dstETag string = sanitizeEtag(copyResponse.ETag)
copyStatus := copyResponse.CopyStatus
var prop blob.GetPropertiesResponse
pollCnt := 0
for copyStatus != nil && *copyStatus == blob.CopyStatusTypePending {
time.Sleep(time.Second * 1)
pollCnt++
prop, err = newBlobClient.GetProperties(context.Background(), &blob.GetPropertiesOptions{
CPKInfo: bb.blobCPKOpt,
})
if err != nil {
log.Err("BlockBlob::RenameFile : CopyStats : Failed to get blob properties for %s [%s]", source, err.Error())
}
copyStatus = prop.CopyStatus
}
if pollCnt > 0 {
dstLMT = prop.LastModified
dstETag = sanitizeEtag(prop.ETag)
}
if copyStatus != nil && *copyStatus == blob.CopyStatusTypeSuccess {
modifyLMTandEtag(srcAttr, dstLMT, dstETag)
}
log.Trace("BlockBlob::RenameFile : %s -> %s done", source, target)
// Copy of the file is done so now delete the older file
err = bb.DeleteFile(source)
for retry := 0; retry < 3 && err == syscall.ENOENT; retry++ {
// Sometimes backend is able to copy source file to destination but when we try to delete the
// source files it returns back with ENOENT. If file was just created on backend it might happen
// that it has not been synced yet at all layers and hence delete is not able to find the source file
log.Trace("BlockBlob::RenameFile : %s -> %s, unable to find source. Retrying %d", source, target, retry)
time.Sleep(1 * time.Second)
err = bb.DeleteFile(source)
}
if err == syscall.ENOENT {
// Even after 3 retries, 1 second apart if server returns 404 then source file no longer
// exists on the backend and its safe to assume rename was successful
err = nil
}
return err
}