2017-07-26 09:29:10 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/rand"
|
2024-12-12 12:12:29 +00:00
|
|
|
"fmt"
|
2017-07-26 09:29:10 +00:00
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
2019-01-27 14:48:43 +00:00
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-07-26 09:29:10 +00:00
|
|
|
)
|
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
const (
|
|
|
|
throughputBufferSize = 1024 * blockSize
|
|
|
|
throughputBufferSizeBits = throughputBufferSize * 8
|
|
|
|
)
|
|
|
|
|
|
|
|
func (s *sparkClient) ExecuteThroughputTest(t *testResult) (err error) {
|
|
|
|
if err = s.runSendTest(t); err != nil {
|
|
|
|
return fmt.Errorf("running send-test: %w", err)
|
2017-07-26 09:29:10 +00:00
|
|
|
}
|
2024-12-12 12:12:29 +00:00
|
|
|
|
|
|
|
if err = s.runRecvTest(t); err != nil {
|
|
|
|
return fmt.Errorf("running recv-test: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2017-07-26 09:29:10 +00:00
|
|
|
}
|
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
//nolint:gocyclo
|
|
|
|
func (s *sparkClient) runSendTest(t *testResult) (err error) {
|
|
|
|
data := make([]byte, throughputBufferSize)
|
|
|
|
if _, err = rand.Read(data); err != nil {
|
|
|
|
return fmt.Errorf("gathering random data: %w", err)
|
2017-07-26 09:29:10 +00:00
|
|
|
}
|
|
|
|
dataReader := bytes.NewReader(data)
|
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
if err = s.connect(); err != nil {
|
|
|
|
return fmt.Errorf("establishing connection: %w", err)
|
2017-07-26 09:29:10 +00:00
|
|
|
}
|
2024-12-12 12:12:29 +00:00
|
|
|
defer func() {
|
|
|
|
if err := s.conn.Close(); err != nil {
|
|
|
|
logrus.WithError(err).Error("closing connection (leaked fd)")
|
|
|
|
}
|
|
|
|
}()
|
2017-07-26 09:29:10 +00:00
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
if err = s.writeCommand("RCV"); err != nil {
|
|
|
|
return fmt.Errorf("sending RCV command: %w", err)
|
2017-07-26 09:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
blockCount int64
|
|
|
|
totalStart = time.Now()
|
|
|
|
)
|
|
|
|
|
|
|
|
for {
|
|
|
|
start := time.Now()
|
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
if _, err = io.Copy(s.conn, dataReader); err != nil {
|
2017-07-26 09:29:10 +00:00
|
|
|
// If we get any of these errors, it probably just means that the server closed the connection
|
|
|
|
if err == io.EOF || err == io.ErrClosedPipe || err == syscall.EPIPE {
|
|
|
|
break
|
|
|
|
}
|
2024-12-12 12:12:29 +00:00
|
|
|
|
2017-07-26 09:29:10 +00:00
|
|
|
if operr, ok := err.(*net.OpError); ok {
|
2024-12-12 12:12:29 +00:00
|
|
|
logrus.Printf("%s", operr.Err)
|
2017-07-26 09:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if operr, ok := err.(*net.OpError); ok && operr.Err.Error() == syscall.ECONNRESET.Error() {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
return fmt.Errorf("copying data: %w", err)
|
2017-07-26 09:29:10 +00:00
|
|
|
}
|
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
bps := float64(throughputBufferSizeBits) / (float64(time.Since(start).Nanoseconds()) / float64(time.Second.Nanoseconds()))
|
2017-07-26 09:29:10 +00:00
|
|
|
if bps < t.Send.Min {
|
|
|
|
t.Send.Min = bps
|
|
|
|
}
|
|
|
|
if bps > t.Send.Max {
|
|
|
|
t.Send.Max = bps
|
|
|
|
}
|
|
|
|
blockCount++
|
|
|
|
|
2019-01-27 14:48:43 +00:00
|
|
|
if _, err := dataReader.Seek(0, 0); err != nil {
|
2024-12-12 12:12:29 +00:00
|
|
|
return fmt.Errorf("seeking data reader: %w", err)
|
2019-01-27 14:48:43 +00:00
|
|
|
}
|
2017-07-26 09:29:10 +00:00
|
|
|
|
|
|
|
if time.Since(totalStart) > time.Duration(throughputTestLength)*time.Second {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// average bit per second
|
2024-12-12 12:12:29 +00:00
|
|
|
t.Send.Avg = float64(throughputBufferSizeBits) / (float64(time.Since(totalStart).Nanoseconds()) / float64(time.Second.Nanoseconds()))
|
2017-07-26 09:29:10 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
func (s *sparkClient) runRecvTest(t *testResult) (err error) {
|
|
|
|
if err = s.connect(); err != nil {
|
|
|
|
return fmt.Errorf("establishing connection: %w", err)
|
2017-07-26 09:29:10 +00:00
|
|
|
}
|
2024-12-12 12:12:29 +00:00
|
|
|
defer func() {
|
|
|
|
if err := s.conn.Close(); err != nil {
|
|
|
|
logrus.WithError(err).Error("closing connection (leaked fd)")
|
|
|
|
}
|
|
|
|
}()
|
2017-07-26 09:29:10 +00:00
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
if err = s.writeCommand("SND"); err != nil {
|
|
|
|
return fmt.Errorf("writing SND command: %w", err)
|
2017-07-26 09:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
blockCount int64
|
|
|
|
totalStart = time.Now()
|
|
|
|
)
|
|
|
|
|
|
|
|
for {
|
|
|
|
start := time.Now()
|
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
if _, err = io.CopyN(io.Discard, s.conn, throughputBufferSize); err != nil {
|
2017-07-26 09:29:10 +00:00
|
|
|
// If we get any of these errors, it probably just means that the server closed the connection
|
|
|
|
if err == io.EOF || err == io.ErrClosedPipe || err == syscall.EPIPE {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if operr, ok := err.(*net.OpError); ok && operr.Err.Error() == syscall.ECONNRESET.Error() {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
return fmt.Errorf("copying data: %w", err)
|
2017-07-26 09:29:10 +00:00
|
|
|
}
|
|
|
|
|
2024-12-12 12:12:29 +00:00
|
|
|
bps := float64(throughputBufferSizeBits) / (float64(time.Since(start).Nanoseconds()) / float64(time.Second.Nanoseconds()))
|
2017-07-26 09:29:10 +00:00
|
|
|
if bps < t.Receive.Min {
|
|
|
|
t.Receive.Min = bps
|
|
|
|
}
|
|
|
|
if bps > t.Receive.Max {
|
|
|
|
t.Receive.Max = bps
|
|
|
|
}
|
|
|
|
blockCount++
|
|
|
|
|
|
|
|
if time.Since(totalStart) > time.Duration(throughputTestLength)*time.Second {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// average bit per second
|
2024-12-12 12:12:29 +00:00
|
|
|
t.Receive.Avg = float64(throughputBufferSizeBits) / (float64(time.Since(totalStart).Nanoseconds()) / float64(time.Second.Nanoseconds()))
|
2017-07-26 09:29:10 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|