mirror of
https://github.com/Oxalide/vsphere-influxdb-go.git
synced 2023-10-10 13:36:51 +02:00
336 lines
6.7 KiB
Go
336 lines
6.7 KiB
Go
|
package stress // import "github.com/influxdata/influxdb/stress"
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// Point is an interface that is used to represent
|
||
|
// the abstract idea of a point in InfluxDB.
|
||
|
type Point interface {
|
||
|
Line() []byte
|
||
|
Graphite() []byte
|
||
|
OpenJSON() []byte
|
||
|
OpenTelnet() []byte
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////
|
||
|
// Example Implementation of the Point Interface //
|
||
|
///////////////////////////////////////////////////
|
||
|
|
||
|
// KeyValue is an intermediate type that is used
|
||
|
// to express Tag and Field similarly.
|
||
|
type KeyValue struct {
|
||
|
Key string
|
||
|
Value string
|
||
|
}
|
||
|
|
||
|
// Tag is a struct for a tag in influxdb.
|
||
|
type Tag KeyValue
|
||
|
|
||
|
// Field is a struct for a field in influxdb.
|
||
|
type Field KeyValue
|
||
|
|
||
|
// Tags is an slice of all the tags for a point.
|
||
|
type Tags []Tag
|
||
|
|
||
|
// Fields is an slice of all the fields for a point.
|
||
|
type Fields []Field
|
||
|
|
||
|
// tagset returns a byte array for a points tagset.
|
||
|
func (t Tags) tagset() []byte {
|
||
|
var buf bytes.Buffer
|
||
|
for _, tag := range t {
|
||
|
buf.Write([]byte(fmt.Sprintf("%v=%v,", tag.Key, tag.Value)))
|
||
|
}
|
||
|
|
||
|
b := buf.Bytes()
|
||
|
b = b[0 : len(b)-1]
|
||
|
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
// fieldset returns a byte array for a points fieldset.
|
||
|
func (f Fields) fieldset() []byte {
|
||
|
var buf bytes.Buffer
|
||
|
for _, field := range f {
|
||
|
buf.Write([]byte(fmt.Sprintf("%v=%v,", field.Key, field.Value)))
|
||
|
}
|
||
|
|
||
|
b := buf.Bytes()
|
||
|
b = b[0 : len(b)-1]
|
||
|
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
// StdPoint represents a point in InfluxDB
|
||
|
type StdPoint struct {
|
||
|
Measurement string
|
||
|
Tags Tags
|
||
|
Fields Fields
|
||
|
Timestamp int64
|
||
|
}
|
||
|
|
||
|
// Line returns a byte array for a point in
|
||
|
// line-protocol format
|
||
|
func (p StdPoint) Line() []byte {
|
||
|
var buf bytes.Buffer
|
||
|
|
||
|
buf.Write([]byte(fmt.Sprintf("%v,", p.Measurement)))
|
||
|
buf.Write(p.Tags.tagset())
|
||
|
buf.Write([]byte(" "))
|
||
|
buf.Write(p.Fields.fieldset())
|
||
|
buf.Write([]byte(" "))
|
||
|
buf.Write([]byte(fmt.Sprintf("%v", p.Timestamp)))
|
||
|
|
||
|
byt := buf.Bytes()
|
||
|
|
||
|
return byt
|
||
|
}
|
||
|
|
||
|
// Graphite returns a byte array for a point
|
||
|
// in graphite-protocol format
|
||
|
func (p StdPoint) Graphite() []byte {
|
||
|
// TODO: implement
|
||
|
// timestamp is at second level resolution
|
||
|
// but can be specified as a float to get nanosecond
|
||
|
// level precision
|
||
|
t := "tag_1.tag_2.measurement[.field] acutal_value timestamp"
|
||
|
return []byte(t)
|
||
|
}
|
||
|
|
||
|
// OpenJSON returns a byte array for a point
|
||
|
// in JSON format
|
||
|
func (p StdPoint) OpenJSON() []byte {
|
||
|
// TODO: implement
|
||
|
//[
|
||
|
// {
|
||
|
// "metric": "sys.cpu.nice",
|
||
|
// "timestamp": 1346846400,
|
||
|
// "value": 18,
|
||
|
// "tags": {
|
||
|
// "host": "web01",
|
||
|
// "dc": "lga"
|
||
|
// }
|
||
|
// },
|
||
|
// {
|
||
|
// "metric": "sys.cpu.nice",
|
||
|
// "timestamp": 1346846400,
|
||
|
// "value": 9,
|
||
|
// "tags": {
|
||
|
// "host": "web02",
|
||
|
// "dc": "lga"
|
||
|
// }
|
||
|
// }
|
||
|
//]
|
||
|
return []byte("hello")
|
||
|
}
|
||
|
|
||
|
// OpenTelnet returns a byte array for a point
|
||
|
// in OpenTSDB-telnet format
|
||
|
func (p StdPoint) OpenTelnet() []byte {
|
||
|
// TODO: implement
|
||
|
// timestamp can be 13 digits at most
|
||
|
// sys.cpu.nice timestamp value tag_key_1=tag_value_1 tag_key_2=tag_value_2
|
||
|
return []byte("hello")
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////
|
||
|
|
||
|
// response is the results making
|
||
|
// a request to influxdb.
|
||
|
type response struct {
|
||
|
Resp *http.Response
|
||
|
Time time.Time
|
||
|
Timer *Timer
|
||
|
}
|
||
|
|
||
|
// Success returns true if the request
|
||
|
// was successful and false otherwise.
|
||
|
func (r response) Success() bool {
|
||
|
// ADD success for tcp, udp, etc
|
||
|
return !(r.Resp == nil || r.Resp.StatusCode != 204)
|
||
|
}
|
||
|
|
||
|
// WriteResponse is a response for a Writer
|
||
|
type WriteResponse response
|
||
|
|
||
|
// QueryResponse is a response for a Querier
|
||
|
type QueryResponse struct {
|
||
|
response
|
||
|
Body string
|
||
|
}
|
||
|
|
||
|
///////////////////////////////
|
||
|
// Definition of the Writer ///
|
||
|
///////////////////////////////
|
||
|
|
||
|
// PointGenerator is an interface for generating points.
|
||
|
type PointGenerator interface {
|
||
|
Generate() (<-chan Point, error)
|
||
|
Time() time.Time
|
||
|
}
|
||
|
|
||
|
// InfluxClient is an interface for writing data to the database.
|
||
|
type InfluxClient interface {
|
||
|
Batch(ps <-chan Point, r chan<- response) error
|
||
|
send(b []byte) (response, error)
|
||
|
//ResponseHandler
|
||
|
}
|
||
|
|
||
|
// Writer is a PointGenerator and an InfluxClient.
|
||
|
type Writer struct {
|
||
|
PointGenerator
|
||
|
InfluxClient
|
||
|
}
|
||
|
|
||
|
// NewWriter returns a Writer.
|
||
|
func NewWriter(p PointGenerator, i InfluxClient) Writer {
|
||
|
w := Writer{
|
||
|
PointGenerator: p,
|
||
|
InfluxClient: i,
|
||
|
}
|
||
|
|
||
|
return w
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// Definition of the Querier ///
|
||
|
////////////////////////////////
|
||
|
|
||
|
// Query is query
|
||
|
type Query string
|
||
|
|
||
|
// QueryGenerator is an interface that is used
|
||
|
// to define queries that will be ran on the DB.
|
||
|
type QueryGenerator interface {
|
||
|
QueryGenerate(f func() time.Time) (<-chan Query, error)
|
||
|
SetTime(t time.Time)
|
||
|
}
|
||
|
|
||
|
// QueryClient is an interface that can write a query
|
||
|
// to an InfluxDB instance.
|
||
|
type QueryClient interface {
|
||
|
Query(q Query) (response, error)
|
||
|
Exec(qs <-chan Query, r chan<- response) error
|
||
|
}
|
||
|
|
||
|
// Querier queries the database.
|
||
|
type Querier struct {
|
||
|
QueryGenerator
|
||
|
QueryClient
|
||
|
}
|
||
|
|
||
|
// NewQuerier returns a Querier.
|
||
|
func NewQuerier(q QueryGenerator, c QueryClient) Querier {
|
||
|
r := Querier{
|
||
|
QueryGenerator: q,
|
||
|
QueryClient: c,
|
||
|
}
|
||
|
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////
|
||
|
// Definition of the Provisioner //
|
||
|
///////////////////////////////////
|
||
|
|
||
|
// Provisioner is an interface that provisions an
|
||
|
// InfluxDB instance
|
||
|
type Provisioner interface {
|
||
|
Provision() error
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////
|
||
|
// Definition of StressTest /////
|
||
|
/////////////////////////////////
|
||
|
|
||
|
// StressTest is a struct that contains all of
|
||
|
// the logic required to execute a Stress Test
|
||
|
type StressTest struct {
|
||
|
Provisioner
|
||
|
Writer
|
||
|
Querier
|
||
|
}
|
||
|
|
||
|
// responseHandler
|
||
|
type responseHandler func(r <-chan response, t *Timer)
|
||
|
|
||
|
// Start executes the Stress Test
|
||
|
func (s *StressTest) Start(wHandle responseHandler, rHandle responseHandler) {
|
||
|
var wg sync.WaitGroup
|
||
|
|
||
|
// Provision the Instance
|
||
|
s.Provision()
|
||
|
|
||
|
wg.Add(1)
|
||
|
// Starts Writing
|
||
|
go func() {
|
||
|
r := make(chan response, 0)
|
||
|
wt := NewTimer()
|
||
|
|
||
|
go func() {
|
||
|
defer wt.StopTimer()
|
||
|
defer close(r)
|
||
|
p, err := s.Generate()
|
||
|
if err != nil {
|
||
|
fmt.Println(err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
err = s.Batch(p, r)
|
||
|
if err != nil {
|
||
|
fmt.Println(err)
|
||
|
return
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// Write Results Handler
|
||
|
wHandle(r, wt)
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
wg.Add(1)
|
||
|
// Starts Querying
|
||
|
go func() {
|
||
|
r := make(chan response, 0)
|
||
|
rt := NewTimer()
|
||
|
|
||
|
go func() {
|
||
|
defer rt.StopTimer()
|
||
|
defer close(r)
|
||
|
q, err := s.QueryGenerate(s.Time)
|
||
|
if err != nil {
|
||
|
fmt.Println(err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
err = s.Exec(q, r)
|
||
|
if err != nil {
|
||
|
fmt.Println(err)
|
||
|
return
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// Read Results Handler
|
||
|
rHandle(r, rt)
|
||
|
wg.Done()
|
||
|
}()
|
||
|
|
||
|
wg.Wait()
|
||
|
}
|
||
|
|
||
|
// NewStressTest returns an instance of a StressTest
|
||
|
func NewStressTest(p Provisioner, w Writer, r Querier) StressTest {
|
||
|
s := StressTest{
|
||
|
Provisioner: p,
|
||
|
Writer: w,
|
||
|
Querier: r,
|
||
|
}
|
||
|
|
||
|
return s
|
||
|
}
|