minio / minio-dotnet

MinIO Client SDK for .NET
https://docs.min.io/docs/dotnet-client-quickstart-guide.html
Apache License 2.0
573 stars 228 forks source link

Bug | SDK does not throw exceptions for upload errors #1159

Open pandapknaepel opened 2 months ago

pandapknaepel commented 2 months ago

We use the Contabo S3 and I can see in the TraceLog that the PUT sometimes fails. However, the SDK does not seem to handle the invalid status, which leads to further errors in our system.

As a workaround, we have written a Verify method that uses the PublicUrl of a file to download it and check the StatusCode.

Here is an example trace log of a request.

-------- Minio Request Log --------
Method: PUT
Resource: /pd-factory-data/storage/printroyal/1227869/1227869_preview_TTF_SZ-WA_DE.jpg
URI: https://eu2.contabostorage.com/pd-factory-data/storage/printroyal/1227869/1227869_preview_TTF_SZ-WA_DE.jpg
Parameters:
    - x-amz-content-sha256: System.String[] (System.Collections.Generic.KeyValuePair`2[System.String,System.Collections.Generic.IEnumerable`1[System.String]])
    - Host: System.String[] (System.Collections.Generic.KeyValuePair`2[System.String,System.Collections.Generic.IEnumerable`1[System.String]])
    - x-amz-date: System.String[] (System.Collections.Generic.KeyValuePair`2[System.String,System.Collections.Generic.IEnumerable`1[System.String]])
    - Authorization: System.String[] (System.Collections.Generic.KeyValuePair`2[System.String,System.Collections.Generic.IEnumerable`1[System.String]])
    - User-Agent: System.String[] (System.Collections.Generic.KeyValuePair`2[System.String,System.Collections.Generic.IEnumerable`1[System.String]])
-------- Minio Response Log --------
Status Code: ServiceUnavailable (503)
Response URI: https://eu2.contabostorage.com/pd-factory-data/storage/printroyal/1227869/1227869_preview_TTF_SZ-WA_DE.jpg
Duration: 20.0768 ms
Headers:
    - Content-Type: application/xml
    - Content-Length: 206
    - ratelimit-reset: 1
    - x-ratelimit-remaining-second: 249
    - x-ratelimit-limit-second: 250
    - ratelimit-limit: 250
    - ratelimit-remaining: 249
    - x-amz-request-id: tx00000aa467ee34b519276-0066c32ff2-13e1c09-default
    - Accept-Ranges: bytes
    - Date: Mon, 19 Aug 2024 11:43:46 GMT
    - Access-Control-Allow-Origin: *
    - Access-Control-Expose-Headers: *
    - Strict-Transport-Security: max-age=16000000; includeSubDomains; preload;
Response Content:
<?xml version="1.0" encoding="UTF-8"?><Error><Code>SlowDown</Code><Message></Message><RequestId>tx00000aa467ee34b519276-0066c32ff2-13e1c09-default</RequestId><HostId>13e1c09-default-default</HostId></Error>
-----------------------------------

My code is following (splitted parts):

internal ContaboS3Service(ContaboS3Config amazonS3Config, IMinioLogger minioLogger)
{
    _minioClient = new MinioClient()
        .WithEndpoint(amazonS3Config.Endpoint)
        .WithCredentials(amazonS3Config.AccessKey, amazonS3Config.SecretKey)
        .WithSSL()
        .Build();
    _minioClient.SetTraceOn(minioLogger);
}
private async Task<PutObjectResponse> PutObjectAsync(
    string bucketName,
    string key, 
    Stream inputStream,
    CancellationToken cancellationToken)
{
    var putObjectArgs = new PutObjectArgs();
    putObjectArgs.WithBucket(bucketName);
    putObjectArgs.WithObject(key);
    putObjectArgs.WithObjectSize(inputStream.Length);
    putObjectArgs.WithStreamData(inputStream);
    putObjectArgs.WithContentType(ContentTypeHelper.DetermineContentTypeFromFileName(key));
    return await _minioClient.PutObjectAsync(putObjectArgs, cancellationToken);
}
private async Task VerifyFileUploadedAsync(
        object uploadKey,
        string destFilePath,
        FileInfo fileInfo,
        UploadType uploadTypeModel,
        UploadsNew uploadModel,
        IStorageService storageService,
        CancellationToken cancellationToken)
    {
        var uri = uploadModel.File?.GetUri(uploadTypeModel.StorageLocation);
        if (storageService is ContaboS3Service && uri != null)
        {
            logger.LogInformation(
                "{ServiceName} | Checking if contabo file was uploaded correctly. UploadKey: {UploadKey}",
                nameof(PandaUploadService), uploadKey);

            using var httpClient = new HttpClient();
            var response = await httpClient.GetAsync(uri, cancellationToken);
            if (response.IsSuccessStatusCode)
            {
                logger.LogInformation(
                    "{ServiceName} | File {DestFilePath} was uploaded correctly. UploadKey: {UploadKey}",
                    nameof(PandaUploadService), destFilePath, uploadKey);
            }
            else
            {
                logger.LogError("{ServiceName} | Failed to upload file {DestFilePath}. UploadKey: {UploadKey}. FileInfo file size: {FileSize}",
                    nameof(PandaUploadService), destFilePath, uploadKey, fileInfo.Size);

                throw new Exception($"File {destFilePath} was not uploaded correctly.");
            }
        }

The output of the verify method is

 "PandaUploadService" | Failed to upload file "storage/printroyal/1227869/1227869_preview_TTF_SZ-WA_DE.jpg". UploadKey: "1227869". FileInfo file size: 529759 

So we can see that the SDK still processes a non-OK status code without throwing an exception.

pandapknaepel commented 2 months ago

Could be related to the ticket: https://github.com/minio/minio-dotnet/issues/1131

@ebozduman

ebozduman commented 2 months ago

@pandapknaepel

You are right. This is also related to the #1118 of which #1131 is a duplicate. We are working on a fix for 1118

codewing commented 1 week ago

Also doesn't throw an error when uploading when the region is not set in the client but the server has one. It just fails silently