mirror of
https://github.com/TwiN/gatus.git
synced 2024-11-21 23:43:27 +01:00
perf(storage): Improve benchmarks and fix race condition
This commit is contained in:
parent
6d64c3c250
commit
fea95b8479
@ -13,10 +13,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
firstCondition = core.Condition("[STATUS] == 200")
|
|
||||||
secondCondition = core.Condition("[RESPONSE_TIME] < 500")
|
|
||||||
thirdCondition = core.Condition("[CERTIFICATE_EXPIRATION] < 72h")
|
|
||||||
|
|
||||||
timestamp = time.Now()
|
timestamp = time.Now()
|
||||||
|
|
||||||
testEndpoint = core.Endpoint{
|
testEndpoint = core.Endpoint{
|
||||||
@ -26,7 +22,7 @@ var (
|
|||||||
Method: "GET",
|
Method: "GET",
|
||||||
Body: "body",
|
Body: "body",
|
||||||
Interval: 30 * time.Second,
|
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,
|
Alerts: nil,
|
||||||
NumberOfFailuresInARow: 0,
|
NumberOfFailuresInARow: 0,
|
||||||
NumberOfSuccessesInARow: 0,
|
NumberOfSuccessesInARow: 0,
|
||||||
|
@ -89,7 +89,7 @@ type Endpoint struct {
|
|||||||
Interval time.Duration `yaml:"interval,omitempty"`
|
Interval time.Duration `yaml:"interval,omitempty"`
|
||||||
|
|
||||||
// Conditions used to determine the health of the endpoint
|
// 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 is the alerting configuration for the endpoint in case of failure
|
||||||
Alerts []*alert.Alert `yaml:"alerts,omitempty"`
|
Alerts []*alert.Alert `yaml:"alerts,omitempty"`
|
||||||
|
@ -80,11 +80,10 @@ func TestEndpoint_Type(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEndpoint_ValidateAndSetDefaults(t *testing.T) {
|
func TestEndpoint_ValidateAndSetDefaults(t *testing.T) {
|
||||||
condition := Condition("[STATUS] == 200")
|
|
||||||
endpoint := Endpoint{
|
endpoint := Endpoint{
|
||||||
Name: "website-health",
|
Name: "website-health",
|
||||||
URL: "https://twin.sh/health",
|
URL: "https://twin.sh/health",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{Condition("[STATUS] == 200")},
|
||||||
Alerts: []*alert.Alert{{Type: alert.TypePagerDuty}},
|
Alerts: []*alert.Alert{{Type: alert.TypePagerDuty}},
|
||||||
}
|
}
|
||||||
endpoint.ValidateAndSetDefaults()
|
endpoint.ValidateAndSetDefaults()
|
||||||
@ -129,7 +128,7 @@ func TestEndpoint_ValidateAndSetDefaultsWithClientConfig(t *testing.T) {
|
|||||||
endpoint := Endpoint{
|
endpoint := Endpoint{
|
||||||
Name: "website-health",
|
Name: "website-health",
|
||||||
URL: "https://twin.sh/health",
|
URL: "https://twin.sh/health",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{condition},
|
||||||
ClientConfig: &client.Config{
|
ClientConfig: &client.Config{
|
||||||
Insecure: true,
|
Insecure: true,
|
||||||
IgnoreRedirect: true,
|
IgnoreRedirect: true,
|
||||||
@ -158,7 +157,7 @@ func TestEndpoint_ValidateAndSetDefaultsWithNoName(t *testing.T) {
|
|||||||
endpoint := &Endpoint{
|
endpoint := &Endpoint{
|
||||||
Name: "",
|
Name: "",
|
||||||
URL: "http://example.com",
|
URL: "http://example.com",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{condition},
|
||||||
}
|
}
|
||||||
err := endpoint.ValidateAndSetDefaults()
|
err := endpoint.ValidateAndSetDefaults()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -172,7 +171,7 @@ func TestEndpoint_ValidateAndSetDefaultsWithNoUrl(t *testing.T) {
|
|||||||
endpoint := &Endpoint{
|
endpoint := &Endpoint{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
URL: "",
|
URL: "",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{condition},
|
||||||
}
|
}
|
||||||
err := endpoint.ValidateAndSetDefaults()
|
err := endpoint.ValidateAndSetDefaults()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -194,7 +193,6 @@ func TestEndpoint_ValidateAndSetDefaultsWithNoConditions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEndpoint_ValidateAndSetDefaultsWithDNS(t *testing.T) {
|
func TestEndpoint_ValidateAndSetDefaultsWithDNS(t *testing.T) {
|
||||||
conditionSuccess := Condition("[DNS_RCODE] == NOERROR")
|
|
||||||
endpoint := &Endpoint{
|
endpoint := &Endpoint{
|
||||||
Name: "dns-test",
|
Name: "dns-test",
|
||||||
URL: "http://example.com",
|
URL: "http://example.com",
|
||||||
@ -202,7 +200,7 @@ func TestEndpoint_ValidateAndSetDefaultsWithDNS(t *testing.T) {
|
|||||||
QueryType: "A",
|
QueryType: "A",
|
||||||
QueryName: "example.com",
|
QueryName: "example.com",
|
||||||
},
|
},
|
||||||
Conditions: []*Condition{&conditionSuccess},
|
Conditions: []Condition{Condition("[DNS_RCODE] == NOERROR")},
|
||||||
}
|
}
|
||||||
err := endpoint.ValidateAndSetDefaults()
|
err := endpoint.ValidateAndSetDefaults()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -218,7 +216,7 @@ func TestEndpoint_buildHTTPRequest(t *testing.T) {
|
|||||||
endpoint := Endpoint{
|
endpoint := Endpoint{
|
||||||
Name: "website-health",
|
Name: "website-health",
|
||||||
URL: "https://twin.sh/health",
|
URL: "https://twin.sh/health",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{condition},
|
||||||
}
|
}
|
||||||
endpoint.ValidateAndSetDefaults()
|
endpoint.ValidateAndSetDefaults()
|
||||||
request := endpoint.buildHTTPRequest()
|
request := endpoint.buildHTTPRequest()
|
||||||
@ -238,7 +236,7 @@ func TestEndpoint_buildHTTPRequestWithCustomUserAgent(t *testing.T) {
|
|||||||
endpoint := Endpoint{
|
endpoint := Endpoint{
|
||||||
Name: "website-health",
|
Name: "website-health",
|
||||||
URL: "https://twin.sh/health",
|
URL: "https://twin.sh/health",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{condition},
|
||||||
Headers: map[string]string{
|
Headers: map[string]string{
|
||||||
"User-Agent": "Test/2.0",
|
"User-Agent": "Test/2.0",
|
||||||
},
|
},
|
||||||
@ -262,7 +260,7 @@ func TestEndpoint_buildHTTPRequestWithHostHeader(t *testing.T) {
|
|||||||
Name: "website-health",
|
Name: "website-health",
|
||||||
URL: "https://twin.sh/health",
|
URL: "https://twin.sh/health",
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{condition},
|
||||||
Headers: map[string]string{
|
Headers: map[string]string{
|
||||||
"Host": "example.com",
|
"Host": "example.com",
|
||||||
},
|
},
|
||||||
@ -283,7 +281,7 @@ func TestEndpoint_buildHTTPRequestWithGraphQLEnabled(t *testing.T) {
|
|||||||
Name: "website-graphql",
|
Name: "website-graphql",
|
||||||
URL: "https://twin.sh/graphql",
|
URL: "https://twin.sh/graphql",
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{condition},
|
||||||
GraphQL: true,
|
GraphQL: true,
|
||||||
Body: `{
|
Body: `{
|
||||||
users(gender: "female") {
|
users(gender: "female") {
|
||||||
@ -314,7 +312,7 @@ func TestIntegrationEvaluateHealth(t *testing.T) {
|
|||||||
endpoint := Endpoint{
|
endpoint := Endpoint{
|
||||||
Name: "website-health",
|
Name: "website-health",
|
||||||
URL: "https://twin.sh/health",
|
URL: "https://twin.sh/health",
|
||||||
Conditions: []*Condition{&condition, &bodyCondition},
|
Conditions: []Condition{condition, bodyCondition},
|
||||||
}
|
}
|
||||||
endpoint.ValidateAndSetDefaults()
|
endpoint.ValidateAndSetDefaults()
|
||||||
result := endpoint.EvaluateHealth()
|
result := endpoint.EvaluateHealth()
|
||||||
@ -337,7 +335,7 @@ func TestIntegrationEvaluateHealthWithFailure(t *testing.T) {
|
|||||||
endpoint := Endpoint{
|
endpoint := Endpoint{
|
||||||
Name: "website-health",
|
Name: "website-health",
|
||||||
URL: "https://twin.sh/health",
|
URL: "https://twin.sh/health",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{condition},
|
||||||
}
|
}
|
||||||
endpoint.ValidateAndSetDefaults()
|
endpoint.ValidateAndSetDefaults()
|
||||||
result := endpoint.EvaluateHealth()
|
result := endpoint.EvaluateHealth()
|
||||||
@ -357,7 +355,7 @@ func TestIntegrationEvaluateHealthWithInvalidCondition(t *testing.T) {
|
|||||||
endpoint := Endpoint{
|
endpoint := Endpoint{
|
||||||
Name: "invalid-condition",
|
Name: "invalid-condition",
|
||||||
URL: "https://twin.sh/health",
|
URL: "https://twin.sh/health",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{condition},
|
||||||
}
|
}
|
||||||
if err := endpoint.ValidateAndSetDefaults(); err != nil {
|
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...
|
// 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{
|
endpoint := Endpoint{
|
||||||
Name: "invalid-host",
|
Name: "invalid-host",
|
||||||
URL: "http://invalid/health",
|
URL: "http://invalid/health",
|
||||||
Conditions: []*Condition{&condition},
|
Conditions: []Condition{condition},
|
||||||
UIConfig: &ui.Config{
|
UIConfig: &ui.Config{
|
||||||
HideHostname: true,
|
HideHostname: true,
|
||||||
},
|
},
|
||||||
@ -408,7 +406,7 @@ func TestIntegrationEvaluateHealthForDNS(t *testing.T) {
|
|||||||
QueryType: "A",
|
QueryType: "A",
|
||||||
QueryName: "example.com.",
|
QueryName: "example.com.",
|
||||||
},
|
},
|
||||||
Conditions: []*Condition{&conditionSuccess, &conditionBody},
|
Conditions: []Condition{conditionSuccess, conditionBody},
|
||||||
}
|
}
|
||||||
endpoint.ValidateAndSetDefaults()
|
endpoint.ValidateAndSetDefaults()
|
||||||
result := endpoint.EvaluateHealth()
|
result := endpoint.EvaluateHealth()
|
||||||
@ -428,7 +426,7 @@ func TestIntegrationEvaluateHealthForICMP(t *testing.T) {
|
|||||||
endpoint := Endpoint{
|
endpoint := Endpoint{
|
||||||
Name: "icmp-test",
|
Name: "icmp-test",
|
||||||
URL: "icmp://127.0.0.1",
|
URL: "icmp://127.0.0.1",
|
||||||
Conditions: []*Condition{&conditionSuccess},
|
Conditions: []Condition{conditionSuccess},
|
||||||
}
|
}
|
||||||
endpoint.ValidateAndSetDefaults()
|
endpoint.ValidateAndSetDefaults()
|
||||||
result := endpoint.EvaluateHealth()
|
result := endpoint.EvaluateHealth()
|
||||||
@ -448,7 +446,7 @@ func TestEndpoint_getIP(t *testing.T) {
|
|||||||
endpoint := Endpoint{
|
endpoint := Endpoint{
|
||||||
Name: "invalid-url-test",
|
Name: "invalid-url-test",
|
||||||
URL: "",
|
URL: "",
|
||||||
Conditions: []*Condition{&conditionSuccess},
|
Conditions: []Condition{conditionSuccess},
|
||||||
}
|
}
|
||||||
result := &Result{}
|
result := &Result{}
|
||||||
endpoint.getIP(result)
|
endpoint.getIP(result)
|
||||||
@ -461,22 +459,22 @@ func TestEndpoint_NeedsToReadBody(t *testing.T) {
|
|||||||
statusCondition := Condition("[STATUS] == 200")
|
statusCondition := Condition("[STATUS] == 200")
|
||||||
bodyCondition := Condition("[BODY].status == UP")
|
bodyCondition := Condition("[BODY].status == UP")
|
||||||
bodyConditionWithLength := Condition("len([BODY].tags) > 0")
|
bodyConditionWithLength := Condition("len([BODY].tags) > 0")
|
||||||
if (&Endpoint{Conditions: []*Condition{&statusCondition}}).needsToReadBody() {
|
if (&Endpoint{Conditions: []Condition{statusCondition}}).needsToReadBody() {
|
||||||
t.Error("expected false, got true")
|
t.Error("expected false, got true")
|
||||||
}
|
}
|
||||||
if !(&Endpoint{Conditions: []*Condition{&bodyCondition}}).needsToReadBody() {
|
if !(&Endpoint{Conditions: []Condition{bodyCondition}}).needsToReadBody() {
|
||||||
t.Error("expected true, got false")
|
t.Error("expected true, got false")
|
||||||
}
|
}
|
||||||
if !(&Endpoint{Conditions: []*Condition{&bodyConditionWithLength}}).needsToReadBody() {
|
if !(&Endpoint{Conditions: []Condition{bodyConditionWithLength}}).needsToReadBody() {
|
||||||
t.Error("expected true, got false")
|
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")
|
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")
|
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")
|
t.Error("expected true, got false")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ var (
|
|||||||
Method: "GET",
|
Method: "GET",
|
||||||
Body: "body",
|
Body: "body",
|
||||||
Interval: 30 * time.Second,
|
Interval: 30 * time.Second,
|
||||||
Conditions: []*core.Condition{&firstCondition, &secondCondition, &thirdCondition},
|
Conditions: []core.Condition{firstCondition, secondCondition, thirdCondition},
|
||||||
Alerts: nil,
|
Alerts: nil,
|
||||||
NumberOfFailuresInARow: 0,
|
NumberOfFailuresInARow: 0,
|
||||||
NumberOfSuccessesInARow: 0,
|
NumberOfSuccessesInARow: 0,
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -579,7 +580,7 @@ func (s *Store) getEndpointResultsByEndpointID(tx *sql.Tx, endpointID int64, pag
|
|||||||
WHERE endpoint_result_id IN (`
|
WHERE endpoint_result_id IN (`
|
||||||
index := 1
|
index := 1
|
||||||
for endpointResultID := range idResultMap {
|
for endpointResultID := range idResultMap {
|
||||||
query += fmt.Sprintf("$%d,", index)
|
query += "$" + strconv.Itoa(index) + ","
|
||||||
args = append(args, endpointResultID)
|
args = append(args, endpointResultID)
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ var (
|
|||||||
Method: "GET",
|
Method: "GET",
|
||||||
Body: "body",
|
Body: "body",
|
||||||
Interval: 30 * time.Second,
|
Interval: 30 * time.Second,
|
||||||
Conditions: []*core.Condition{&firstCondition, &secondCondition, &thirdCondition},
|
Conditions: []core.Condition{firstCondition, secondCondition, thirdCondition},
|
||||||
Alerts: nil,
|
Alerts: nil,
|
||||||
NumberOfFailuresInARow: 0,
|
NumberOfFailuresInARow: 0,
|
||||||
NumberOfSuccessesInARow: 0,
|
NumberOfSuccessesInARow: 0,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -48,23 +49,34 @@ func BenchmarkStore_GetAllEndpointStatuses(b *testing.B) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, scenario := range scenarios {
|
for _, scenario := range scenarios {
|
||||||
scenario.Store.Insert(&testEndpoint, &testSuccessfulResult)
|
numberOfEndpoints := []int{10, 25, 50, 100}
|
||||||
scenario.Store.Insert(&testEndpoint, &testUnsuccessfulResult)
|
for _, numberOfEndpointsToCreate := range numberOfEndpoints {
|
||||||
b.Run(scenario.Name, func(b *testing.B) {
|
// Create endpoints and insert results
|
||||||
if scenario.Parallel {
|
for i := 0; i < numberOfEndpointsToCreate; i++ {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
endpoint := testEndpoint
|
||||||
for pb.Next() {
|
endpoint.Name = "endpoint" + strconv.Itoa(i)
|
||||||
scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
|
// Insert 20 results for each endpoint
|
||||||
}
|
for j := 0; j < 20; j++ {
|
||||||
})
|
scenario.Store.Insert(&endpoint, &testSuccessfulResult)
|
||||||
} else {
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.ReportAllocs()
|
// Run the scenarios
|
||||||
})
|
b.Run(scenario.Name+"-with-"+strconv.Itoa(numberOfEndpointsToCreate)+"-endpoints", func(b *testing.B) {
|
||||||
scenario.Store.Clear()
|
if scenario.Parallel {
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, _ = scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
_, _ = scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.ReportAllocs()
|
||||||
|
})
|
||||||
|
scenario.Store.Clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ var (
|
|||||||
Method: "GET",
|
Method: "GET",
|
||||||
Body: "body",
|
Body: "body",
|
||||||
Interval: 30 * time.Second,
|
Interval: 30 * time.Second,
|
||||||
Conditions: []*core.Condition{&firstCondition, &secondCondition, &thirdCondition},
|
Conditions: []core.Condition{firstCondition, secondCondition, thirdCondition},
|
||||||
Alerts: nil,
|
Alerts: nil,
|
||||||
NumberOfFailuresInARow: 0,
|
NumberOfFailuresInARow: 0,
|
||||||
NumberOfSuccessesInARow: 0,
|
NumberOfSuccessesInARow: 0,
|
||||||
|
Loading…
Reference in New Issue
Block a user