Skip to content

Commit 5e80643

Browse files
committed
Add QOS API's
1 parent 54af66a commit 5e80643

File tree

3 files changed

+200
-6
lines changed

3 files changed

+200
-6
lines changed

api-bucket-qos.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
3+
* Copyright 2025 MinIO, Inc.
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package minio
17+
18+
import (
19+
"context"
20+
"io"
21+
"net/http"
22+
"net/url"
23+
"strings"
24+
25+
"github.com/minio/minio-go/v7/internal/json"
26+
"github.com/minio/minio-go/v7/pkg/s3utils"
27+
"gopkg.in/yaml.v3"
28+
)
29+
30+
// QOSConfigVersionCurrent is the current version of the QoS configuration.
31+
const QOSConfigVersionCurrent = "v1"
32+
33+
// QOSConfig represents the QoS configuration for a bucket.
34+
type QOSConfig struct {
35+
Version string `yaml:"version"`
36+
Rules []QOSRule `yaml:"rules"`
37+
}
38+
39+
// QOSRule represents a single QoS rule.
40+
type QOSRule struct {
41+
ID string `yaml:"id"`
42+
Label string `yaml:"label,omitempty"`
43+
Priority int `yaml:"priority"`
44+
ObjectPrefix string `yaml:"objectPrefix"`
45+
API string `yaml:"api"`
46+
Rate int64 `yaml:"rate"`
47+
Burst int64 `yaml:"burst"` // not required for concurrency limit
48+
Limit string `yaml:"limit"` // "concurrency" or "rps"
49+
}
50+
51+
// NewQOSConfig creates a new empty QoS configuration.
52+
func NewQOSConfig() *QOSConfig {
53+
return &QOSConfig{
54+
Version: "v1",
55+
Rules: []QOSRule{},
56+
}
57+
}
58+
59+
// GetBucketQOS retrieves the QoS configuration for the bucket.
60+
func (c *Client) GetBucketQOS(ctx context.Context, bucket string) (*QOSConfig, error) {
61+
var qosCfg QOSConfig
62+
// Input validation.
63+
if err := s3utils.CheckValidBucketName(bucket); err != nil {
64+
return nil, err
65+
}
66+
urlValues := make(url.Values)
67+
urlValues.Set("qos", "")
68+
// Execute GET on bucket to fetch qos.
69+
resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{
70+
bucketName: bucket,
71+
queryValues: urlValues,
72+
contentSHA256Hex: emptySHA256Hex,
73+
})
74+
defer closeResponse(resp)
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
if resp.StatusCode != http.StatusOK {
80+
return nil, httpRespToErrorResponse(resp, bucket, "")
81+
}
82+
b, err := io.ReadAll(resp.Body)
83+
if err != nil {
84+
return nil, err
85+
}
86+
87+
if err = yaml.Unmarshal(b, &qosCfg); err != nil {
88+
return nil, err
89+
}
90+
91+
return &qosCfg, nil
92+
}
93+
94+
// SetBucketQOS sets the QoS configuration for a bucket.
95+
func (c *Client) SetBucketQOS(ctx context.Context, bucket string, qosCfg *QOSConfig) error {
96+
// Input validation.
97+
if err := s3utils.CheckValidBucketName(bucket); err != nil {
98+
return err
99+
}
100+
101+
data, err := yaml.Marshal(qosCfg)
102+
if err != nil {
103+
return err
104+
}
105+
// Get resources properly escaped and lined up before
106+
// using them in http request.
107+
urlValues := make(url.Values)
108+
urlValues.Set("qos", "")
109+
110+
reqMetadata := requestMetadata{
111+
bucketName: bucket,
112+
queryValues: urlValues,
113+
contentBody: strings.NewReader(string(data)),
114+
contentLength: int64(len(data)),
115+
}
116+
117+
// Execute PUT to upload a new bucket QoS configuration.
118+
resp, err := c.executeMethod(ctx, http.MethodPut, reqMetadata)
119+
defer closeResponse(resp)
120+
if err != nil {
121+
return err
122+
}
123+
if resp != nil {
124+
if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK {
125+
return httpRespToErrorResponse(resp, bucket, "")
126+
}
127+
}
128+
return nil
129+
}
130+
131+
// CounterMetric returns stats for a counter
132+
type CounterMetric struct {
133+
Last1m uint64 `json:"last1m"`
134+
Last1hr uint64 `json:"last1hr"`
135+
Total uint64 `json:"total"`
136+
}
137+
138+
// QOSMetric - metric for a qos rule per bucket
139+
type QOSMetric struct {
140+
APIName string `json:"apiName"`
141+
Rule QOSRule `json:"rule"`
142+
Totals CounterMetric `json:"totals"`
143+
Throttled CounterMetric `json:"throttleCount"`
144+
ExceededRateLimit CounterMetric `json:"exceededRateLimitCount"`
145+
ClientDisconnCount CounterMetric `json:"clientDisconnectCount"`
146+
ReqTimeoutCount CounterMetric `json:"reqTimeoutCount"`
147+
}
148+
149+
// QOSNodeStats represents stats for a bucket on a single node
150+
type QOSNodeStats struct {
151+
Stats []QOSMetric `json:"stats"`
152+
NodeName string `json:"node"`
153+
}
154+
155+
// GetBucketQOSMetrics fetches bucket QoS metrics
156+
func (c *Client) GetBucketQOSMetrics(ctx context.Context, bucketName, nodeName string) (qs []QOSNodeStats, err error) {
157+
// Input validation.
158+
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
159+
return qs, err
160+
}
161+
// Get resources properly escaped and lined up before
162+
// using them in http request.
163+
urlValues := make(url.Values)
164+
urlValues.Set("qos-metrics", "")
165+
if nodeName != "" {
166+
urlValues.Set("node", nodeName)
167+
}
168+
// Execute GET on bucket to get qos metrics.
169+
resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{
170+
bucketName: bucketName,
171+
queryValues: urlValues,
172+
})
173+
174+
defer closeResponse(resp)
175+
if err != nil {
176+
return qs, err
177+
}
178+
179+
if resp.StatusCode != http.StatusOK {
180+
return qs, httpRespToErrorResponse(resp, bucketName, "")
181+
}
182+
respBytes, err := io.ReadAll(resp.Body)
183+
if err != nil {
184+
return qs, err
185+
}
186+
187+
if err := json.Unmarshal(respBytes, &qs); err != nil {
188+
return qs, err
189+
}
190+
return qs, nil
191+
}

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ require (
1515
github.com/minio/md5-simd v1.1.2
1616
github.com/rs/xid v1.6.0
1717
github.com/tinylib/msgp v1.3.0
18-
golang.org/x/crypto v0.39.0
19-
golang.org/x/net v0.41.0
18+
golang.org/x/crypto v0.36.0
19+
golang.org/x/net v0.38.0
20+
gopkg.in/yaml.v3 v3.0.1
2021
)
2122

2223
require (

go.sum

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
2929
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
3030
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
3131
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
32-
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
33-
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
34-
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
35-
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
32+
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
33+
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
34+
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
35+
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
3636
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
3737
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
3838
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
3939
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
40+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
41+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
4042
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
4143
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)