perf(storage): Improve benchmarks and fix race condition

This commit is contained in:
TwiN 2022-06-13 19:15:30 -04:00
parent 6d64c3c250
commit fea95b8479
8 changed files with 56 additions and 49 deletions

View File

@ -13,10 +13,6 @@ import (
)
var (
firstCondition = core.Condition("[STATUS] == 200")
secondCondition = core.Condition("[RESPONSE_TIME] < 500")
thirdCondition = core.Condition("[CERTIFICATE_EXPIRATION] < 72h")
timestamp = time.Now()
testEndpoint = core.Endpoint{
@ -26,7 +22,7 @@ var (
Method: "GET",
Body: "body",
Interval: 30 * time.Second,
Conditions: []*core.Condition{&firstCondition, &secondCondition, &thirdCondition},
Conditions: []core.Condition{core.Condition("[STATUS] == 200"), core.Condition("[RESPONSE_TIME] < 500"), core.Condition("[CERTIFICATE_EXPIRATION] < 72h")},
Alerts: nil,
NumberOfFailuresInARow: 0,
NumberOfSuccessesInARow: 0,

View File

@ -89,7 +89,7 @@ type Endpoint struct {
Interval time.Duration `yaml:"interval,omitempty"`
// Conditions used to determine the health of the endpoint
Conditions []*Condition `yaml:"conditions"`
Conditions []Condition `yaml:"conditions"`
// Alerts is the alerting configuration for the endpoint in case of failure
Alerts []*alert.Alert `yaml:"alerts,omitempty"`

View File

@ -80,11 +80,10 @@ func TestEndpoint_Type(t *testing.T) {
}
func TestEndpoint_ValidateAndSetDefaults(t *testing.T) {
condition := Condition("[STATUS] == 200")
endpoint := Endpoint{
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
Conditions: []Condition{Condition("[STATUS] == 200")},
Alerts: []*alert.Alert{{Type: alert.TypePagerDuty}},
}
endpoint.ValidateAndSetDefaults()
@ -129,7 +128,7 @@ func TestEndpoint_ValidateAndSetDefaultsWithClientConfig(t *testing.T) {
endpoint := Endpoint{
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
Conditions: []Condition{condition},
ClientConfig: &client.Config{
Insecure: true,
IgnoreRedirect: true,
@ -158,7 +157,7 @@ func TestEndpoint_ValidateAndSetDefaultsWithNoName(t *testing.T) {
endpoint := &Endpoint{
Name: "",
URL: "http://example.com",
Conditions: []*Condition{&condition},
Conditions: []Condition{condition},
}
err := endpoint.ValidateAndSetDefaults()
if err == nil {
@ -172,7 +171,7 @@ func TestEndpoint_ValidateAndSetDefaultsWithNoUrl(t *testing.T) {
endpoint := &Endpoint{
Name: "example",
URL: "",
Conditions: []*Condition{&condition},
Conditions: []Condition{condition},
}
err := endpoint.ValidateAndSetDefaults()
if err == nil {
@ -194,7 +193,6 @@ func TestEndpoint_ValidateAndSetDefaultsWithNoConditions(t *testing.T) {
}
func TestEndpoint_ValidateAndSetDefaultsWithDNS(t *testing.T) {
conditionSuccess := Condition("[DNS_RCODE] == NOERROR")
endpoint := &Endpoint{
Name: "dns-test",
URL: "http://example.com",
@ -202,7 +200,7 @@ func TestEndpoint_ValidateAndSetDefaultsWithDNS(t *testing.T) {
QueryType: "A",
QueryName: "example.com",
},
Conditions: []*Condition{&conditionSuccess},
Conditions: []Condition{Condition("[DNS_RCODE] == NOERROR")},
}
err := endpoint.ValidateAndSetDefaults()
if err != nil {
@ -218,7 +216,7 @@ func TestEndpoint_buildHTTPRequest(t *testing.T) {
endpoint := Endpoint{
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
Conditions: []Condition{condition},
}
endpoint.ValidateAndSetDefaults()
request := endpoint.buildHTTPRequest()
@ -238,7 +236,7 @@ func TestEndpoint_buildHTTPRequestWithCustomUserAgent(t *testing.T) {
endpoint := Endpoint{
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
Conditions: []Condition{condition},
Headers: map[string]string{
"User-Agent": "Test/2.0",
},
@ -262,7 +260,7 @@ func TestEndpoint_buildHTTPRequestWithHostHeader(t *testing.T) {
Name: "website-health",
URL: "https://twin.sh/health",
Method: "POST",
Conditions: []*Condition{&condition},
Conditions: []Condition{condition},
Headers: map[string]string{
"Host": "example.com",
},
@ -283,7 +281,7 @@ func TestEndpoint_buildHTTPRequestWithGraphQLEnabled(t *testing.T) {
Name: "website-graphql",
URL: "https://twin.sh/graphql",
Method: "POST",
Conditions: []*Condition{&condition},
Conditions: []Condition{condition},
GraphQL: true,
Body: `{
users(gender: "female") {
@ -314,7 +312,7 @@ func TestIntegrationEvaluateHealth(t *testing.T) {
endpoint := Endpoint{
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition, &bodyCondition},
Conditions: []Condition{condition, bodyCondition},
}
endpoint.ValidateAndSetDefaults()
result := endpoint.EvaluateHealth()
@ -337,7 +335,7 @@ func TestIntegrationEvaluateHealthWithFailure(t *testing.T) {
endpoint := Endpoint{
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
Conditions: []Condition{condition},
}
endpoint.ValidateAndSetDefaults()
result := endpoint.EvaluateHealth()
@ -357,7 +355,7 @@ func TestIntegrationEvaluateHealthWithInvalidCondition(t *testing.T) {
endpoint := Endpoint{
Name: "invalid-condition",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
Conditions: []Condition{condition},
}
if err := endpoint.ValidateAndSetDefaults(); err != nil {
// XXX: Should this really not return an error? After all, the condition is not valid and conditions are part of the endpoint...
@ -377,7 +375,7 @@ func TestIntegrationEvaluateHealthWithError(t *testing.T) {
endpoint := Endpoint{
Name: "invalid-host",
URL: "http://invalid/health",
Conditions: []*Condition{&condition},
Conditions: []Condition{condition},
UIConfig: &ui.Config{
HideHostname: true,
},
@ -408,7 +406,7 @@ func TestIntegrationEvaluateHealthForDNS(t *testing.T) {
QueryType: "A",
QueryName: "example.com.",
},
Conditions: []*Condition{&conditionSuccess, &conditionBody},
Conditions: []Condition{conditionSuccess, conditionBody},
}
endpoint.ValidateAndSetDefaults()
result := endpoint.EvaluateHealth()
@ -428,7 +426,7 @@ func TestIntegrationEvaluateHealthForICMP(t *testing.T) {
endpoint := Endpoint{
Name: "icmp-test",
URL: "icmp://127.0.0.1",
Conditions: []*Condition{&conditionSuccess},
Conditions: []Condition{conditionSuccess},
}
endpoint.ValidateAndSetDefaults()
result := endpoint.EvaluateHealth()
@ -448,7 +446,7 @@ func TestEndpoint_getIP(t *testing.T) {
endpoint := Endpoint{
Name: "invalid-url-test",
URL: "",
Conditions: []*Condition{&conditionSuccess},
Conditions: []Condition{conditionSuccess},
}
result := &Result{}
endpoint.getIP(result)
@ -461,22 +459,22 @@ func TestEndpoint_NeedsToReadBody(t *testing.T) {
statusCondition := Condition("[STATUS] == 200")
bodyCondition := Condition("[BODY].status == UP")
bodyConditionWithLength := Condition("len([BODY].tags) > 0")
if (&Endpoint{Conditions: []*Condition{&statusCondition}}).needsToReadBody() {
if (&Endpoint{Conditions: []Condition{statusCondition}}).needsToReadBody() {
t.Error("expected false, got true")
}
if !(&Endpoint{Conditions: []*Condition{&bodyCondition}}).needsToReadBody() {
if !(&Endpoint{Conditions: []Condition{bodyCondition}}).needsToReadBody() {
t.Error("expected true, got false")
}
if !(&Endpoint{Conditions: []*Condition{&bodyConditionWithLength}}).needsToReadBody() {
if !(&Endpoint{Conditions: []Condition{bodyConditionWithLength}}).needsToReadBody() {
t.Error("expected true, got false")
}
if !(&Endpoint{Conditions: []*Condition{&statusCondition, &bodyCondition}}).needsToReadBody() {
if !(&Endpoint{Conditions: []Condition{statusCondition, bodyCondition}}).needsToReadBody() {
t.Error("expected true, got false")
}
if !(&Endpoint{Conditions: []*Condition{&bodyCondition, &statusCondition}}).needsToReadBody() {
if !(&Endpoint{Conditions: []Condition{bodyCondition, statusCondition}}).needsToReadBody() {
t.Error("expected true, got false")
}
if !(&Endpoint{Conditions: []*Condition{&bodyConditionWithLength, &statusCondition}}).needsToReadBody() {
if !(&Endpoint{Conditions: []Condition{bodyConditionWithLength, statusCondition}}).needsToReadBody() {
t.Error("expected true, got false")
}
}

View File

@ -22,7 +22,7 @@ var (
Method: "GET",
Body: "body",
Interval: 30 * time.Second,
Conditions: []*core.Condition{&firstCondition, &secondCondition, &thirdCondition},
Conditions: []core.Condition{firstCondition, secondCondition, thirdCondition},
Alerts: nil,
NumberOfFailuresInARow: 0,
NumberOfSuccessesInARow: 0,

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
"strconv"
"strings"
"time"
@ -579,7 +580,7 @@ func (s *Store) getEndpointResultsByEndpointID(tx *sql.Tx, endpointID int64, pag
WHERE endpoint_result_id IN (`
index := 1
for endpointResultID := range idResultMap {
query += fmt.Sprintf("$%d,", index)
query += "$" + strconv.Itoa(index) + ","
args = append(args, endpointResultID)
index++
}

View File

@ -23,7 +23,7 @@ var (
Method: "GET",
Body: "body",
Interval: 30 * time.Second,
Conditions: []*core.Condition{&firstCondition, &secondCondition, &thirdCondition},
Conditions: []core.Condition{firstCondition, secondCondition, thirdCondition},
Alerts: nil,
NumberOfFailuresInARow: 0,
NumberOfSuccessesInARow: 0,

View File

@ -1,6 +1,7 @@
package store
import (
"strconv"
"testing"
"time"
@ -48,24 +49,35 @@ func BenchmarkStore_GetAllEndpointStatuses(b *testing.B) {
},
}
for _, scenario := range scenarios {
scenario.Store.Insert(&testEndpoint, &testSuccessfulResult)
scenario.Store.Insert(&testEndpoint, &testUnsuccessfulResult)
b.Run(scenario.Name, func(b *testing.B) {
numberOfEndpoints := []int{10, 25, 50, 100}
for _, numberOfEndpointsToCreate := range numberOfEndpoints {
// Create endpoints and insert results
for i := 0; i < numberOfEndpointsToCreate; i++ {
endpoint := testEndpoint
endpoint.Name = "endpoint" + strconv.Itoa(i)
// Insert 20 results for each endpoint
for j := 0; j < 20; j++ {
scenario.Store.Insert(&endpoint, &testSuccessfulResult)
}
}
// Run the scenarios
b.Run(scenario.Name+"-with-"+strconv.Itoa(numberOfEndpointsToCreate)+"-endpoints", func(b *testing.B) {
if scenario.Parallel {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
_, _ = scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
}
})
} else {
for n := 0; n < b.N; n++ {
scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
_, _ = scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
}
}
b.ReportAllocs()
})
scenario.Store.Clear()
}
}
}
func BenchmarkStore_Insert(b *testing.B) {

View File

@ -26,7 +26,7 @@ var (
Method: "GET",
Body: "body",
Interval: 30 * time.Second,
Conditions: []*core.Condition{&firstCondition, &secondCondition, &thirdCondition},
Conditions: []core.Condition{firstCondition, secondCondition, thirdCondition},
Alerts: nil,
NumberOfFailuresInARow: 0,
NumberOfSuccessesInARow: 0,