github.com/snowflakedb/gosnowflake@v1.9.0/storage_client.go (about) 1 // Copyright (c) 2021-2022 Snowflake Computing Inc. All rights reserved. 2 3 package gosnowflake 4 5 import ( 6 "bytes" 7 "fmt" 8 "math" 9 "os" 10 "path" 11 "path/filepath" 12 "time" 13 ) 14 15 const ( 16 defaultConcurrency = 1 17 defaultMaxRetry = 5 18 ) 19 20 // implemented by localUtil and remoteStorageUtil 21 type storageUtil interface { 22 createClient(*execResponseStageInfo, bool) (cloudClient, error) 23 uploadOneFileWithRetry(*fileMetadata) error 24 downloadOneFile(*fileMetadata) error 25 } 26 27 // implemented by snowflakeS3Util, snowflakeAzureUtil and snowflakeGcsUtil 28 type cloudUtil interface { 29 createClient(*execResponseStageInfo, bool) (cloudClient, error) 30 getFileHeader(*fileMetadata, string) (*fileHeader, error) 31 uploadFile(string, *fileMetadata, *encryptMetadata, int, int64) error 32 nativeDownloadFile(*fileMetadata, string, int64) error 33 } 34 35 type cloudClient interface{} 36 37 type remoteStorageUtil struct { 38 } 39 40 func (rsu *remoteStorageUtil) getNativeCloudType(cli string) cloudUtil { 41 if cloudType(cli) == s3Client { 42 return &snowflakeS3Client{} 43 } else if cloudType(cli) == azureClient { 44 return &snowflakeAzureClient{} 45 } else if cloudType(cli) == gcsClient { 46 return &snowflakeGcsClient{} 47 } 48 return nil 49 } 50 51 // call cloud utils' native create client methods 52 func (rsu *remoteStorageUtil) createClient(info *execResponseStageInfo, useAccelerateEndpoint bool) (cloudClient, error) { 53 utilClass := rsu.getNativeCloudType(info.LocationType) 54 return utilClass.createClient(info, useAccelerateEndpoint) 55 } 56 57 func (rsu *remoteStorageUtil) uploadOneFile(meta *fileMetadata) error { 58 var encryptMeta *encryptMetadata 59 var dataFile string 60 var err error 61 if meta.encryptionMaterial != nil { 62 if meta.srcStream != nil { 63 var encryptedStream bytes.Buffer 64 srcStream := meta.srcStream 65 if meta.realSrcStream != nil { 66 srcStream = meta.realSrcStream 67 } 68 encryptMeta, err = encryptStream(meta.encryptionMaterial, srcStream, &encryptedStream, 0) 69 if err != nil { 70 return err 71 } 72 meta.realSrcStream = &encryptedStream 73 dataFile = meta.realSrcFileName 74 } else { 75 encryptMeta, dataFile, err = encryptFile(meta.encryptionMaterial, meta.realSrcFileName, 0, meta.tmpDir) 76 if err != nil { 77 return err 78 } 79 } 80 } else { 81 dataFile = meta.realSrcFileName 82 } 83 84 utilClass := rsu.getNativeCloudType(meta.stageInfo.LocationType) 85 maxConcurrency := int(meta.parallel) 86 var lastErr error 87 maxRetry := defaultMaxRetry 88 for retry := 0; retry < maxRetry; retry++ { 89 if !meta.overwrite { 90 header, err := utilClass.getFileHeader(meta, meta.dstFileName) 91 if meta.resStatus == notFoundFile { 92 err := utilClass.uploadFile(dataFile, meta, encryptMeta, maxConcurrency, meta.options.MultiPartThreshold) 93 if err != nil { 94 logger.Warnf("Error uploading %v. err: %v", dataFile, err) 95 } 96 } else if err != nil { 97 return err 98 } 99 if header != nil && meta.resStatus == uploaded { 100 meta.dstFileSize = 0 101 meta.resStatus = skipped 102 return nil 103 } 104 } 105 if meta.overwrite || meta.resStatus == notFoundFile { 106 err := utilClass.uploadFile(dataFile, meta, encryptMeta, maxConcurrency, meta.options.MultiPartThreshold) 107 if err != nil { 108 logger.Debugf("Error uploading %v. err: %v", dataFile, err) 109 } 110 } 111 if meta.resStatus == uploaded || meta.resStatus == renewToken || meta.resStatus == renewPresignedURL { 112 return nil 113 } else if meta.resStatus == needRetry { 114 if !meta.noSleepingTime { 115 sleepingTime := intMin(int(math.Exp2(float64(retry))), 16) 116 time.Sleep(time.Second * time.Duration(sleepingTime)) 117 } 118 } else if meta.resStatus == needRetryWithLowerConcurrency { 119 maxConcurrency = int(meta.parallel) - (retry * int(meta.parallel) / maxRetry) 120 maxConcurrency = intMax(defaultConcurrency, maxConcurrency) 121 meta.lastMaxConcurrency = maxConcurrency 122 123 if !meta.noSleepingTime { 124 sleepingTime := intMin(int(math.Exp2(float64(retry))), 16) 125 time.Sleep(time.Second * time.Duration(sleepingTime)) 126 } 127 } 128 lastErr = meta.lastError 129 } 130 if lastErr != nil { 131 return lastErr 132 } 133 return fmt.Errorf("unkown error uploading %v", dataFile) 134 } 135 136 func (rsu *remoteStorageUtil) uploadOneFileWithRetry(meta *fileMetadata) error { 137 utilClass := rsu.getNativeCloudType(meta.stageInfo.LocationType) 138 retryOuter := true 139 for i := 0; i < 10; i++ { 140 // retry 141 if err := rsu.uploadOneFile(meta); err != nil { 142 return err 143 } 144 retryInner := true 145 if meta.resStatus == uploaded || meta.resStatus == skipped { 146 for j := 0; j < 10; j++ { 147 status := meta.resStatus 148 utilClass.getFileHeader(meta, meta.dstFileName) 149 // check file header status and verify upload/skip 150 if meta.resStatus == notFoundFile { 151 time.Sleep(time.Second) // wait 1 second 152 continue 153 } else { 154 retryInner = false 155 meta.resStatus = status 156 break 157 } 158 } 159 } 160 if !retryInner { 161 retryOuter = false 162 break 163 } else { 164 continue 165 } 166 } 167 if retryOuter { 168 // wanted to continue retrying but could not upload/find file 169 meta.resStatus = errStatus 170 } 171 return nil 172 } 173 174 func (rsu *remoteStorageUtil) downloadOneFile(meta *fileMetadata) error { 175 fullDstFileName := path.Join(meta.localLocation, baseName(meta.dstFileName)) 176 fullDstFileName, err := expandUser(fullDstFileName) 177 if err != nil { 178 return err 179 } 180 if !filepath.IsAbs(fullDstFileName) { 181 cwd, err := os.Getwd() 182 if err != nil { 183 return err 184 } 185 fullDstFileName = filepath.Join(cwd, fullDstFileName) 186 } 187 baseDir, err := getDirectory() 188 if err != nil { 189 return err 190 } 191 if _, err = os.Stat(baseDir); os.IsNotExist(err) { 192 if err = os.MkdirAll(baseDir, os.ModePerm); err != nil { 193 return err 194 } 195 } 196 197 utilClass := rsu.getNativeCloudType(meta.stageInfo.LocationType) 198 header, err := utilClass.getFileHeader(meta, meta.srcFileName) 199 if err != nil { 200 return err 201 } 202 if header != nil { 203 meta.srcFileSize = header.contentLength 204 } 205 206 maxConcurrency := meta.parallel 207 var lastErr error 208 maxRetry := defaultMaxRetry 209 for retry := 0; retry < maxRetry; retry++ { 210 if err = utilClass.nativeDownloadFile(meta, fullDstFileName, maxConcurrency); err != nil { 211 return err 212 } 213 if meta.resStatus == downloaded { 214 if meta.encryptionMaterial != nil { 215 if meta.presignedURL != nil { 216 header, err = utilClass.getFileHeader(meta, meta.srcFileName) 217 if err != nil { 218 return err 219 } 220 } 221 tmpDstFileName, err := decryptFile(header.encryptionMetadata, 222 meta.encryptionMaterial, fullDstFileName, 0, meta.tmpDir) 223 if err != nil { 224 return err 225 } 226 if err = os.Rename(tmpDstFileName, fullDstFileName); err != nil { 227 return err 228 } 229 } 230 if fi, err := os.Stat(fullDstFileName); err == nil { 231 meta.dstFileSize = fi.Size() 232 } 233 return nil 234 } 235 lastErr = meta.lastError 236 } 237 if lastErr != nil { 238 return lastErr 239 } 240 return fmt.Errorf("unkown error downloading %v", fullDstFileName) 241 }