File: perf-client.go

package info (click to toggle)
golang-github-minio-madmin-go 3.0.104-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,380 kB
  • sloc: python: 801; makefile: 6
file content (135 lines) | stat: -rw-r--r-- 3,581 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//
// Copyright (c) 2015-2024 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

package madmin

import (
	"context"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/dustin/go-humanize"
)

// ClientPerfExtraTime - time for get lock or other
type ClientPerfExtraTime struct {
	TimeSpent int64 `json:"dur,omitempty"`
}

// ClientPerfResult - stats from  client to server
type ClientPerfResult struct {
	Endpoint  string `json:"endpoint,omitempty"`
	Error     string `json:"error,omitempty"`
	BytesSend uint64
	TimeSpent int64
}

// clientPerfReader - wrap the reader
type clientPerfReader struct {
	count     uint64
	startTime time.Time
	endTime   time.Time
	buf       []byte
}

// Start - reader start
func (c *clientPerfReader) Start() {
	buf := make([]byte, 128*humanize.KiByte)
	rand.Read(buf)
	c.buf = buf
	c.startTime = time.Now()
}

// End - reader end
func (c *clientPerfReader) End() {
	c.endTime = time.Now()
}

// Read - reader send data
func (c *clientPerfReader) Read(p []byte) (n int, err error) {
	n = copy(p, c.buf)
	c.count += uint64(n)
	return n, nil
}

var _ io.Reader = &clientPerfReader{}

const (
	// MaxClientPerfTimeout for max time out for client perf
	MaxClientPerfTimeout = time.Second * 30
	// MinClientPerfTimeout for min time out for client perf
	MinClientPerfTimeout = time.Second * 5
)

// ClientPerf - perform net from client to MinIO servers
func (adm *AdminClient) ClientPerf(ctx context.Context, dur time.Duration) (result ClientPerfResult, err error) {
	if dur > MaxClientPerfTimeout {
		dur = MaxClientPerfTimeout
	}
	if dur < MinClientPerfTimeout {
		dur = MinClientPerfTimeout
	}
	ctx, cancel := context.WithTimeout(ctx, dur)
	defer cancel()
	queryVals := make(url.Values)
	reader := &clientPerfReader{}
	reader.Start()
	_, err = adm.executeMethod(ctx, http.MethodPost, requestData{
		queryValues:   queryVals,
		relPath:       adminAPIPrefix + "/speedtest/client/devnull",
		contentReader: reader,
	})
	reader.End()
	if errors.Is(err, context.DeadlineExceeded) && ctx.Err() != nil {
		err = nil
	}

	resp, err := adm.executeMethod(context.Background(), http.MethodPost, requestData{
		queryValues: queryVals,
		relPath:     adminAPIPrefix + "/speedtest/client/devnull/extratime",
	})
	if err != nil {
		return ClientPerfResult{}, err
	}
	var extraTime ClientPerfExtraTime
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&extraTime)
	if err != nil {
		return ClientPerfResult{}, err
	}
	durSpend := reader.endTime.Sub(reader.startTime).Nanoseconds()
	if extraTime.TimeSpent > 0 {
		durSpend = durSpend - extraTime.TimeSpent
	}
	if durSpend <= 0 {
		return ClientPerfResult{}, fmt.Errorf("unexpected spent time duration, mostly NTP errors on the server")
	}
	return ClientPerfResult{
		BytesSend: reader.count,
		TimeSpent: durSpend,
		Error:     "",
		Endpoint:  adm.endpointURL.String(),
	}, err
}