Skip to content

Commit c7a64f8

Browse files
committed
Add QOS API's
1 parent ec103a7 commit c7a64f8

File tree

3 files changed

+201
-6
lines changed

3 files changed

+201
-6
lines changed

api-bucket-qos.go

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

go.mod

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

2122
require (

go.sum

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

0 commit comments

Comments
 (0)