minio / mc

Unix like utilities for object store
https://min.io/download
GNU Affero General Public License v3.0
2.86k stars 548 forks source link

Enabled `mc quota` to set bucket throttle config #4780

Closed shtripat closed 10 months ago

shtripat commented 11 months ago

Community Contribution License

All community contributions in this pull request are licensed to the project maintainers under the terms of the [Apache 2 license] (https://www.apache.org/licenses/LICENSE-2.0). By creating this pull request I represent that I have the right to license the contributions to the project maintainers under the Apache 2 license.

Description

Motivation and Context

How to test this PR?

  1. mc quota set m1/test-bucket --concurrent-requests-count 200 --apis "PutObject,DeleteObject,Get*" --size 2GB
  2. mc quota set m1/test-bucket --throttle-rules-file ./trules.json Sample trules.json
    [
    {
    "concurrentRequestsCount": 100,
    "apis": [
      "PutObject",
      "ListObjects"
    ]
    },
    {
    "concurrentRequestsCount": 100,
    "apis": [
      "Get*"
    ]
    }
    ]
  3. mc quota info m1/test-bucket
    Bucket `test-bucket` has hard quota of 1.9 GiB
    Throttle configuration:
    - ID: clsp0hdb4dtl5hl26380, Concurrent Requests Count: 200, APIs: DeleteObject,Get*,PutObject
  4. mc quota info m1/test-bucket --json
    {
    "status": "success",
    "bucket": "test-bucket",
    "quota": 2000000000,
    "type": "hard",
    "throttleRules": [
    {
    "ID": "clsp0hdb4dtl5hl26380",
    "concurrentRequestsCount": 200,
    "apis": [
    "DeleteObject",
    "Get*",
    "PutObject"
    ]
    }
    ]
    }
  5. Test throttle quota in effect Set really small values for --concurrent-requests-count for few APIs (say PutObject and HeadObject) as below mc quota set m1/test-bucket --concurrent-requests-count 10 --apis "PutObject,Get*,Head*,List*" Run mc admin trace m1 -v from another terminal session for detailed view of S3 calls and request/response. Now use below sample go code to simulate parallel requests to bucket and you should be seeing few Throttle quota exceeded. Applied RuleID: clsp1btb4dtl7p6ad190, Allowed Requests Count: 10, Current Requets Count: 59, API: PutObject errors. For this there are sample 1MB files (count 500) under ./data dir
package main

import (
        "context"
        "fmt"
        "os"
        "sync"

        minio "github.com/minio/minio-go/v7"
        "github.com/minio/minio-go/v7/pkg/credentials"
)

func main() {
        var wg sync.WaitGroup
        wg.Add(500)

        for i:=0; i<500; i++ {
                objName := fmt.Sprintf("file%d", i+1)
                fileName := fmt.Sprintf("./data/file%d", i+1)
                go func(obj, file string) {
                        defer wg.Done()
                        endpoint := "localhost:9000"
                        accessKeyID := "minioadmin"
                        secretAccessKey := "minioadmin"
                        useSSL := false

                        // Initialize minio client object.
                        minioClient, err := minio.New(endpoint, &minio.Options{
                                Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
                                Secure: useSSL,
                        })
                        if err != nil {
                                fmt.Println(err)
                                return
                        }
                        ctx := context.Background()

                        f, err := os.Open(file)
                        if err != nil {
                                fmt.Println(err)
                                return
                        }
                        defer f.Close()
                        fStat, err := f.Stat()
                        if err != nil {
                                fmt.Println(err)
                                return
                        }

                        info, err := minioClient.PutObject(ctx, "test-bucket", obj, f, fStat.Size(),
                                            minio.PutObjectOptions{ContentType:"application/octet-stream"})
                        if err != nil {
                              fmt.Println(err)
                              return
                        }
                        fmt.Println("Loaded: ", info)
                }(objName, fileName)
        }
        wg.Wait()
}
package main

import (
        "context"
        "fmt"
        "io"
        "os"
        "sync"

        minio "github.com/minio/minio-go/v7"
        "github.com/minio/minio-go/v7/pkg/credentials"
)

func main() {
        var wg sync.WaitGroup
        wg.Add(500)

        for i:=0; i<500; i++ {
                go func() {
                        defer wg.Done()
                        endpoint := "localhost:9000"
                        accessKeyID := "minioadmin"
                        secretAccessKey := "minioadmin"
                        useSSL := false

                        // Initialize minio client object.
                        minioClient, err := minio.New(endpoint, &minio.Options{
                                Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
                                Secure: useSSL,
                        })
                        if err != nil {
                                fmt.Println(err)
                                return
                        }
                        ctx := context.Background()

                        obj, err := minioClient.GetObject(ctx, "test-bucket", "issue", minio.GetObjectOptions{})
                        if err != nil {
                                fmt.Println(err)
                                return
                        }
                        defer obj.Close()

                        localFile, err := os.Create("a.txt")
                        if err != nil {
                            fmt.Println(err)
                            return
                        }
                        defer localFile.Close()

                        if _, err = io.Copy(localFile, obj); err != nil {
                            fmt.Println(err)
                            return
                        }
                        fmt.Println("Read the object and copied locally")             
                }()
        }
        wg.Wait()
}

Also in case if throttle quota breach MinIO would show errors as below

API: PutObject(bucket=test-bucket, object=file392)
Time: 11:10:31 UTC 12/13/2023
DeploymentID: cd195cc1-ad90-4697-8665-1b0c15952c98
RequestID: 17A05FA750155169
RemoteHost: [::1]
Host: localhost:9000
UserAgent: MinIO (linux; amd64) minio-go/v7.0.63
Error: Throttle quota exceeded. Applied RuleID: clsp1btb4dtl7p6ad190, Allowed Requests Count: 10, Current Requets Count: 66, API: PutObject (cmd.BucketThrottleQuotaExceeded)

And MinIO trace should show details as below

localhost:9000 [REQUEST s3.PutObject] [2023-12-13T16:40:31.353] [Client IP: [::1]]
localhost:9000 PUT /test-bucket/file478
localhost:9000 Proto: HTTP/1.1
localhost:9000 Host: localhost:9000
localhost:9000 Authorization: AWS4-HMAC-SHA256 Credential=minioadmin/20231213/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length,Signature=cc131066ee54b77cc221ab31f6bd71c55675e9f81972693e9c1998cdbe6bca5f
localhost:9000 Content-Length: 201
localhost:9000 Content-Type: application/octet-stream
localhost:9000 User-Agent: MinIO (linux; amd64) minio-go/v7.0.63
localhost:9000 X-Amz-Content-Sha256: STREAMING-AWS4-HMAC-SHA256-PAYLOAD
localhost:9000 X-Amz-Date: 20231213T111031Z
localhost:9000 X-Amz-Decoded-Content-Length: 28
localhost:9000 <BLOB>
localhost:9000 [RESPONSE] [2023-12-13T16:40:31.375] [ Duration 21.199ms TTFB 21.192972ms ↑ 119 B  ↓ 471 B ]
localhost:9000 400 Bad Request
localhost:9000 Content-Length: 471
localhost:9000 Content-Type: application/xml
localhost:9000 X-Amz-Id-2: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8
localhost:9000 X-Amz-Request-Id: 17A05FA75016DC42
localhost:9000 X-Content-Type-Options: nosniff
localhost:9000 Accept-Ranges: bytes
localhost:9000 Server: MinIO
localhost:9000 Strict-Transport-Security: max-age=31536000; includeSubDomains
localhost:9000 Vary: Origin,Accept-Encoding
localhost:9000 X-Xss-Protection: 1; mode=block
localhost:9000 <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>XMinioAdminBucketThrottleQuotaExceeded</Code><Message>Throttle quota exceeded. Applied RuleID: clsp1btb4dtl7p6ad190, Allowed Requests Count: 10, Current Requets Count: 67, API: PutObject</Message><Key>file478</Key><BucketName>test-bucket</BucketName><Resource>/test-bucket/file478</Resource><RequestId>17A05FA75016DC42</RequestId><HostId>dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8</HostId></Error>

Needs https://github.com/minio/minio/pull/18595, https://github.com/minio/madmin-go/pull/252 and https://github.com/minio/console/pull/3160

Types of changes

Checklist:

shtripat commented 10 months ago

Closing as not needed.