113 lines
2.8 KiB
Go
113 lines
2.8 KiB
Go
package stressClient
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// ###############################################
|
|
// A selection of methods to manage the write path
|
|
// ###############################################
|
|
|
|
// Packages up Package from channel in goroutine
|
|
func (sc *stressClient) spinOffWritePackage(p Package, serv int) {
|
|
sc.Add(1)
|
|
sc.wc.Increment()
|
|
go func() {
|
|
sc.retry(p, time.Duration(time.Nanosecond), serv)
|
|
sc.Done()
|
|
sc.wc.Decrement()
|
|
}()
|
|
}
|
|
|
|
// Implements backoff and retry logic for 500 responses
|
|
func (sc *stressClient) retry(p Package, backoff time.Duration, serv int) {
|
|
|
|
// Set Backoff Interval to 500ms
|
|
backoffInterval := time.Duration(500 * time.Millisecond)
|
|
|
|
// Arithmetic backoff for kicks
|
|
bo := backoff + backoffInterval
|
|
|
|
// Make the write request
|
|
resp, elapsed, err := sc.prepareWrite(p.Body, serv)
|
|
|
|
// Find number of times request has been retried
|
|
numBackoffs := int(bo/backoffInterval) - 1
|
|
|
|
// On 500 responses, resp == nil. This logic keeps program for panicing
|
|
var statusCode int
|
|
|
|
if resp == nil {
|
|
statusCode = 500
|
|
} else {
|
|
statusCode = resp.StatusCode
|
|
}
|
|
|
|
// Make a point for reporting
|
|
point := sc.writePoint(numBackoffs, p.StatementID, statusCode, elapsed, p.Tracer.Tags, len(p.Body))
|
|
|
|
// Send the Response(point, tracer)
|
|
sc.responseChan <- NewResponse(point, p.Tracer)
|
|
|
|
// BatchInterval enforcement
|
|
bi, _ := time.ParseDuration(sc.wdelay)
|
|
time.Sleep(bi)
|
|
|
|
// Retry if the statusCode was not 204 or the err != nil
|
|
if !(statusCode == 204) || err != nil {
|
|
// Increment the *Tracer waitgroup if we are going to retry the request
|
|
p.Tracer.Add(1)
|
|
// Log the error if there is one
|
|
fmt.Println(err)
|
|
// Backoff enforcement
|
|
time.Sleep(bo)
|
|
sc.retry(p, bo, serv)
|
|
}
|
|
|
|
}
|
|
|
|
// Prepares to send the POST request
|
|
func (sc *stressClient) prepareWrite(points []byte, serv int) (*http.Response, time.Duration, error) {
|
|
|
|
// Construct address string
|
|
var writeTemplate string
|
|
if sc.ssl {
|
|
writeTemplate = "https://%v/write?db=%v&precision=%v&u=%v&p=%v"
|
|
} else {
|
|
writeTemplate = "http://%v/write?db=%v&precision=%v&u=%v&p=%v"
|
|
}
|
|
address := fmt.Sprintf(writeTemplate, sc.addresses[serv], sc.database, sc.precision, sc.username, sc.password)
|
|
|
|
// Start timer
|
|
t := time.Now()
|
|
resp, err := makePost(address, bytes.NewBuffer(points))
|
|
elapsed := time.Since(t)
|
|
|
|
return resp, elapsed, err
|
|
}
|
|
|
|
// Send POST request, read it, and handle errors
|
|
func makePost(url string, points io.Reader) (*http.Response, error) {
|
|
|
|
resp, err := http.Post(url, "text/plain", points)
|
|
|
|
if err != nil {
|
|
return resp, fmt.Errorf("Error making write POST request\n error: %v\n url: %v\n", err, url)
|
|
}
|
|
|
|
body, _ := ioutil.ReadAll(resp.Body)
|
|
|
|
if resp.StatusCode != 204 {
|
|
return resp, fmt.Errorf("Write returned non-204 status code\n StatusCode: %v\n InfluxDB Error: %v\n", resp.StatusCode, string(body))
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return resp, nil
|
|
}
|